From 603df53a8bd3dca984c856e8b950ffc50e41fa87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20M=C3=A9sz=C3=A1ros=20=28Laptop=29?= Date: Sat, 14 Feb 2026 04:41:05 +0100 Subject: [PATCH] Fix chinese paste handling --- src/components/text_area.zig | 37 +++++++++++++++++++++++++++++++++++ src/components/text_input.zig | 30 ++++++++++++++++++++++++++++ tests/input_tests.zig | 20 +++++++++++++++++++ 3 files changed, 87 insertions(+) diff --git a/src/components/text_area.zig b/src/components/text_area.zig index 1789350..684ad32 100644 --- a/src/components/text_area.zig +++ b/src/components/text_area.zig @@ -187,6 +187,7 @@ pub const TextArea = struct { switch (key.key) { .char => |c| self.insertChar(c), + .paste => |text| self.insertText(text), .enter => self.insertNewline(), .backspace => self.deleteBackward(), .delete => self.deleteForward(), @@ -224,6 +225,42 @@ pub const TextArea = struct { self.cursor_col = pos + len; } + fn insertText(self: *TextArea, text: []const u8) void { + var i: usize = 0; + while (i < text.len) { + if (text[i] == '\r') { + self.insertNewline(); + if (i + 1 < text.len and text[i + 1] == '\n') i += 1; + i += 1; + continue; + } + if (text[i] == '\n') { + self.insertNewline(); + i += 1; + continue; + } + + const len = std.unicode.utf8ByteSequenceLength(text[i]) catch { + self.insertChar(text[i]); + i += 1; + continue; + }; + if (i + len > text.len) { + self.insertChar(text[i]); + i += 1; + continue; + } + + const codepoint = std.unicode.utf8Decode(text[i .. i + len]) catch { + self.insertChar(text[i]); + i += 1; + continue; + }; + self.insertChar(codepoint); + i += len; + } + } + fn insertNewline(self: *TextArea) void { if (self.max_lines) |max| { if (self.lines.items.len >= max) return; diff --git a/src/components/text_input.zig b/src/components/text_input.zig index 9e887ed..335273e 100644 --- a/src/components/text_input.zig +++ b/src/components/text_input.zig @@ -216,6 +216,7 @@ pub const TextInput = struct { switch (key.key) { .char => |c| self.insertChar(c), + .paste => |text| self.insertText(text), .backspace => self.deleteBackward(), .delete => self.deleteForward(), .left => self.moveCursorLeft(), @@ -248,6 +249,35 @@ pub const TextInput = struct { self.cursor += len; } + fn insertText(self: *TextInput, text: []const u8) void { + var i: usize = 0; + while (i < text.len) { + if (text[i] == '\r' or text[i] == '\n') { + i += 1; + continue; + } + + const len = std.unicode.utf8ByteSequenceLength(text[i]) catch { + self.insertChar(text[i]); + i += 1; + continue; + }; + if (i + len > text.len) { + self.insertChar(text[i]); + i += 1; + continue; + } + + const codepoint = std.unicode.utf8Decode(text[i .. i + len]) catch { + self.insertChar(text[i]); + i += 1; + continue; + }; + self.insertChar(codepoint); + i += len; + } + } + fn deleteBackward(self: *TextInput) void { if (self.cursor == 0) return; diff --git a/tests/input_tests.zig b/tests/input_tests.zig index 4603f2e..1feeb1f 100644 --- a/tests/input_tests.zig +++ b/tests/input_tests.zig @@ -130,3 +130,23 @@ test "parseAll multiple keys" { try testing.expect(events[0] == .key); try testing.expectEqual(@as(u21, 'a'), events[0].key.key.char); } + +test "TextInput accepts multi-character paste commits (IME-like)" { + var input = zz.TextInput.init(testing.allocator); + defer input.deinit(); + + input.handleKey(.{ .key = .{ .paste = "中文输入" } }); + + try testing.expectEqualStrings("中文输入", input.getValue()); +} + +test "TextArea accepts multi-character paste commits (IME-like)" { + var area = zz.TextArea.init(testing.allocator); + defer area.deinit(); + + area.handleKey(.{ .key = .{ .paste = "中文输入" } }); + + const value = try area.getValue(testing.allocator); + defer testing.allocator.free(value); + try testing.expectEqualStrings("中文输入", value); +}