textRunPopulate
Populate the text_run node with character position and word length details.
Parameters
- self:*AccessKit
- *AccessKit
- text:[]const u8
- []const u8
- opts:TextRunOptions
- TextRunOptions
- text_info:*std.MultiArrayList(CharPositionInfo)
- *std.MultiArrayList(CharPositionInfo)
- r:dvui.Rect.Physical
- dvui.Rect.Physical
Source
Implementation
pub fn textRunPopulate(
self: *AccessKit,
text: []const u8,
opts: TextRunOptions,
text_info: *std.MultiArrayList(CharPositionInfo),
r: dvui.Rect.Physical,
) void {
const window: *dvui.Window = @alignCast(@fieldParentPtr("accesskit", self));
// Word lengths include all punctuation for the word. e.g. `abc"$.` has a length of 6.
var word_starts: std.ArrayList(u8) = .empty;
defer word_starts.deinit(window.arena());
var prev_char_wordbreak: bool = self.text_run_prev_wordbreak or opts.node_parent_id != self.text_run_prev_parent_id;
for (text, 0..) |ch, i| {
if (std.mem.findScalar(u8, dvui.TextLayoutWidget.word_breaks, ch) == null) {
if (prev_char_wordbreak) {
// AK TODO: If a line is more than 255 characters, then it needs to be broken up into 2 text runs.
word_starts.append(window.arena(), @min(i, std.math.maxInt(u8))) catch {};
prev_char_wordbreak = false;
}
} else {
prev_char_wordbreak = true;
}
}
self.text_run_prev_wordbreak = prev_char_wordbreak;
self.text_run_prev_parent_id = opts.node_parent_id;
const ak_node = self.nodes.get(opts.node_id) orelse {
log.debug("textRunPopulate called for non-existent node_id {x}:{d} with parent {x}:{d}\n", .{ opts.node_id, opts.node_id, opts.node_parent_id, opts.node_parent_id });
return;
};
AccessKit.nodeSetRole(ak_node, Role.text_run.asU8());
// Make sure text run is at least 1 pixel wide to cater for newlines.
nodeSetBounds(ak_node, .{ .x0 = r.x, .y0 = r.y, .x1 = r.x + @max(r.w, 1), .y1 = r.y + r.h });
nodeSetValueWithLength(ak_node, text.ptr, text.len);
nodeSetCharacterLengths(ak_node, text_info.len, text_info.items(.l).ptr);
nodeSetCharacterWidths(ak_node, text_info.len, text_info.items(.w).ptr);
nodeSetCharacterPositions(ak_node, text_info.len, text_info.items(.x).ptr);
nodeSetWordStarts(ak_node, word_starts.items.len, word_starts.items.ptr);
nodeSetTextDirection(ak_node, AccessKit.TextDirection.left_to_right);
if (self.text_runs.items.len > 0) {
const prev_run = self.text_runs.items[self.text_runs.items.len - 1];
const prev_node = self.nodes.get(prev_run.node_id) orelse unreachable;
// text_runs on the same line should be linked together.
if (prev_run.node_parent_id == opts.node_parent_id and
prev_run.line == opts.line)
{
nodeSetPreviousOnLine(ak_node, prev_run.node_id.asU64());
nodeSetNextOnLine(prev_node, opts.node_id.asU64());
}
}
self.text_runs.append(window.gpa, opts) catch {}; // If text run can't be added, selection actions will fail this frame.
}