bytesNeeded
Parameters
- self:*TextLayoutWidget
- *TextLayoutWidget
- edit_start:usize
- usize
- edit_end:usize
- usize
- edit_added:i64
- i64
Source
Implementation
pub fn bytesNeeded(self: *TextLayoutWidget, edit_start: usize, edit_end: usize, edit_added: i64) ?bytesNeededReturn {
if (self.byte_heights.len == 0) return null;
// intersect our content rect with the clipping rect
const clip_logical = self.data().contentRectScale().rectFromPhysical(dvui.clipGet());
const vr = self.data().contentRect().justSize().intersect(clip_logical);
var start_byte: usize = 0;
var end_byte: usize = self.byte_heights[self.byte_heights.len - 1].byte;
const Context = struct { height: f32, byte: usize };
var context: Context = .{ .height = vr.y, .byte = edit_start };
var sel_end: usize = edit_end;
var end_height = vr.y + vr.h;
if (self.copy_sel) |sel| {
context.byte = @min(context.byte, sel.start);
sel_end = @max(sel_end, sel.end);
}
var include_cursor = self.scroll_to_cursor;
// if we are moving the cursor, need to process the text around where we are moving it
switch (self.sel_move) {
.none => {},
.mouse => {}, // all in visible region, excepted below
.expand_pt => |*ep| {
switch (ep.which) {
.word, .line => {}, // all in visible region
.home, .end => include_cursor = true,
}
},
.char_left_right => include_cursor = true,
.cursor_updown => |*cud| {
if (cud.pt) |p| {
// found cursor last frame, need to include p this frame
context.height = @min(context.height, p.y);
end_height = @max(end_height, p.y);
} else {
// we are looking for the cursor to move from
include_cursor = true;
}
},
.word_left_right => include_cursor = true,
}
if (include_cursor and self.sel_move != .mouse) {
context.byte = @min(context.byte, self.selection.cursor);
sel_end = @max(sel_end, self.selection.cursor);
}
// binary search for the start
const predicateFn = struct {
fn predicateFn(ctx: Context, item: ByteHeight) bool {
return item.height <= ctx.height and item.byte < ctx.byte;
}
}.predicateFn;
var first_past_height = std.sort.partitionPoint(ByteHeight, self.byte_heights, context, predicateFn);
if (first_past_height == self.byte_heights.len) {
// can't start at the final
first_past_height -|= 1;
}
if (first_past_height > 0) {
// starting not at the top
const startBH = self.byte_heights[first_past_height - 1];
start_byte = startBH.byte;
self.insert_pt.y = startBH.height;
self.line = startBH.line;
self.bytes_seen = start_byte;
if (!include_cursor and (self.selection.cursor < self.bytes_seen)) {
std.debug.assert(self.cursor_seen == false);
self.cursor_rect = Rect{ .x = self.insert_pt.x, .y = self.insert_pt.y, .w = 1, .h = 10 };
self.cursorSeen();
}
switch (self.sel_move) {
.word_left_right => |*wlr| {
if (wlr.count < 0) {
// update default so that if someone does tons of word left in
// the same frame (so they move to before we started processing
// text, they will only go back to this index (instead of 0)
for (&wlr.word_start_idx) |*i| {
i.* = start_byte;
}
}
},
else => {},
}
//std.debug.print("setting min height to {d}\n", .{self.insert_pt.y});
// set min height just to make sure it happens
const start_size = self.data().options.padSize(.{ .h = self.insert_pt.y });
self.data().min_size.h = @max(self.data().min_size.h, start_size.h);
// copy all the ByteHeights we skipped
self.byte_heights_new.appendSlice(dvui.currentWindow().arena(), self.byte_heights[0..first_past_height]) catch {};
// copy all the LineAscents we skipped
var i: usize = 0;
while (i < self.line_ascents.len and self.line_ascents[i].line < startBH.line) i += 1;
self.line_ascents_new.appendSlice(dvui.currentWindow().arena(), self.line_ascents[0..i]) catch {};
self.line_ascents_idx = i;
}
// linear scan for the end (but not the final)
for (self.byte_heights[first_past_height .. self.byte_heights.len - 1], first_past_height..) |bh, i| {
if (bh.height >= end_height and bh.byte > sel_end) {
//std.debug.print("found end {d} {d} bh height {d} vr {d} {d} {d}\n", .{ i, self.byte_heights.len, bh.height, vr.y, vr.h, vr.y + vr.h });
end_byte = bh.byte;
self.byte_height_after_idx = i;
break;
}
}
// assume min width stays the same
self.data().min_size.w = (dvui.minSizeGet(self.data().id) orelse Size.all(0)).w;
// adjust end_byte for any edits
if (edit_added >= 0) {
end_byte += @intCast(edit_added);
} else {
end_byte -= @intCast(-edit_added);
}
//std.debug.print("bytesNeeded end {d} {d} {d}\n", .{ start_byte, end_byte, edit_added });
return .{ .start = start_byte, .end = end_byte };
}