DVUI

Parameters

#
self:*TextEntryWidget
*TextEntryWidget

Source

Implementation

#
pub fn draw(self: *TextEntryWidget) void {
    self.drawBeforeText();

    if (self.len == 0) {
        if (self.init_opts.placeholder) |placeholder| {
            if (self.data().accesskit_node()) |ak_node| {
                AccessKit.nodeSetPlaceholderWithLength(ak_node, placeholder.ptr, placeholder.len);

                // Create an empty text run for the empty text entry.
                dvui.currentWindow().accesskit.text_run_parent = self.data().id;
                self.textLayout.textRunCreateEmpty(self.data().id, true);
                // prevent textLayout from making a text run for the placeholder text
                dvui.currentWindow().accesskit.text_run_parent = null;
            }
            self.textLayout.addText(placeholder, .{ .color_text = self.textLayout.data().options.color(.text).opacity(0.65) });
        }
    }

    if (dvui.accesskit_enabled) {
        // parent text runs to us
        dvui.currentWindow().accesskit.text_run_parent = self.data().id;
    }

    if (self.init_opts.password_char) |pc| {
        {
            // adjust selection for obfuscation
            var count: usize = 0;
            var bytes: usize = 0;
            var sel = self.textLayout.selection;
            var sstart: ?usize = null;
            var scursor: ?usize = null;
            var send: ?usize = null;
            var utf8it = (std.unicode.Utf8View.initUnchecked(self.text[0..self.len])).iterator();
            while (utf8it.nextCodepoint()) |codepoint| {
                if (sstart == null and sel.start == bytes) sstart = count * pc.len;
                if (scursor == null and sel.cursor == bytes) scursor = count * pc.len;
                if (send == null and sel.end == bytes) send = count * pc.len;
                count += 1;
                bytes += std.unicode.utf8CodepointSequenceLength(codepoint) catch unreachable;
            } else {
                if (sstart == null and sel.start >= bytes) sstart = count * pc.len;
                if (scursor == null and sel.cursor >= bytes) scursor = count * pc.len;
                if (send == null and sel.end >= bytes) send = count * pc.len;
            }
            sel.start = sstart.?;
            sel.cursor = scursor.?;
            sel.end = send.?;
            const password_str: ?[]u8 = dvui.currentWindow().lifo().alloc(u8, count * pc.len) catch null;
            if (password_str) |pstr| {
                defer dvui.currentWindow().lifo().free(pstr);
                for (0..count) |i| {
                    for (0..pc.len) |pci| {
                        pstr[i * pc.len + pci] = pc[pci];
                    }
                }
                self.textLayout.addText(pstr, self.data().options.strip());
            } else {
                dvui.log.warn("Could not allocate password_str, falling back to one single password_str", .{});
                self.textLayout.addText(pc, self.data().options.strip());
            }
        }

        self.textLayout.addTextDone(self.data().options.strip());

        {
            // reset selection
            var count: usize = 0;
            var bytes: usize = 0;
            var sel = self.textLayout.selection;
            var sstart: ?usize = null;
            var scursor: ?usize = null;
            var send: ?usize = null;
            // NOTE: We assume that all text in the area it valid utf8, loop with exit early on invalid utf8
            var utf8it = (std.unicode.Utf8View.initUnchecked(self.text[0..self.len])).iterator();
            while (utf8it.nextCodepoint()) |codepoint| {
                if (sstart == null and sel.start == count * pc.len) sstart = bytes;
                if (scursor == null and sel.cursor == count * pc.len) scursor = bytes;
                if (send == null and sel.end == count * pc.len) send = bytes;
                count += 1;
                bytes += std.unicode.utf8CodepointSequenceLength(codepoint) catch unreachable;
            } else {
                if (sstart == null and sel.start >= count * pc.len) sstart = bytes;
                if (scursor == null and sel.cursor >= count * pc.len) scursor = bytes;
                if (send == null and sel.end >= count * pc.len) send = bytes;
            }
            sel.start = sstart.?;
            sel.cursor = scursor.?;
            sel.end = send.?;
        }

        self.drawAfterText();
        return;
    }

    // syntax highlighting
    if (dvui.useTreeSitter) {
        if (self.init_opts.tree_sitter) |ts| {
            // parse is lazy
            var iter = ts.parse(self.data().id, "parser", self.text[0..self.len]);
            defer iter.deinit();

            iter.debug = ts.log_captures;

            // reparse if needed
            if (self.text_changed and !dvui.firstFrame(self.data().id)) {
                var edit: ?dvui.c.TSInputEdit = null;
                if (self.init_opts.cache_layout) {
                    edit = @as(dvui.c.TSInputEdit, undefined);
                    edit.?.start_byte = @intCast(self.text_changed_start);
                    edit.?.old_end_byte = @intCast(self.text_changed_end);
                    edit.?.new_end_byte = @intCast(@as(i64, @intCast(self.text_changed_end)) + self.text_changed_added);

                    edit.?.start_point = .{ .row = 0, .column = 0 };
                    edit.?.old_end_point = .{ .row = 0, .column = 0 };
                    edit.?.new_end_point = .{ .row = 0, .column = 0 };
                }

                iter.reparse(edit);
            }

            // set the bytes we need matches for
            if (self.textLayout.cacheLayoutBytes()) |clb| {
                iter.setByteRange(clb.start, clb.end);
            }

            // do all matches
            const normal_opts = self.data().options.strip();
            while (iter.next()) |h| {
                self.textLayout.addText(h.text, h.opts orelse normal_opts);
            }

            self.textLayout.addTextDone(normal_opts);
            self.drawAfterText();
            return;
        }
    }

    // simple text
    self.textLayout.addText(self.text[0..self.len], self.data().options.strip());
    self.textLayout.addTextDone(self.data().options.strip());

    self.drawAfterText();
}