From 7875db0aea79e24da4ac48415fe681029aaa8792 Mon Sep 17 00:00:00 2001 From: Yves Biener Date: Mon, 30 Jun 2025 22:03:59 +0200 Subject: [PATCH] feat(element/input): make accept event agnostic to `u8` and `u21` slices There are now comptime checks in place an the corresponding triggered event will be automatically converted to the correct type to support simple ascii strings (`[]u8`) or utf-8 strings (`[]u21`). --- examples/elements/input.zig | 7 ++--- src/element.zig | 56 +++++++++++++++++++++++++++---------- 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/examples/elements/input.zig b/examples/elements/input.zig index 4c8d3e8..e13cb28 100644 --- a/examples/elements/input.zig +++ b/examples/elements/input.zig @@ -127,10 +127,7 @@ pub fn main() !void { .key => |key| if (key.eql(.{ .cp = 'c', .mod = .{ .ctrl = true } })) app.quit(), .accept => |input| { defer allocator.free(input); - var string = try allocator.alloc(u8, input.len); - defer allocator.free(string); - for (0.., input) |i, char| string[i] = @intCast(char); - log.debug("Accepted input '{s}'", .{string}); + log.debug("Accepted input '{s}'", .{input}); }, .err => |err| log.err("Received {s} with message: {s}", .{ @errorName(err.err), err.msg }), else => {}, @@ -165,5 +162,5 @@ const zterm = @import("zterm"); const Color = zterm.Color; const App = zterm.App(union(enum) { - accept: []u21, + accept: []u8, }); diff --git a/src/element.zig b/src/element.zig index 31c33e0..b47f79f 100644 --- a/src/element.zig +++ b/src/element.zig @@ -365,11 +365,31 @@ pub fn Scrollable(Event: type) type { }; } -pub fn Input(Event: type, Queue: type) fn (accept_event: meta.FieldEnum(Event)) type { +pub fn Input(Event: type, Queue: type) fn (meta.FieldEnum(Event)) type { + // NOTE the struct is necessary, as otherwise I cannot point to the function I want to return const input_struct = struct { pub fn input_fn(accept_event: meta.FieldEnum(Event)) type { - // TODO create `comptime` check for `accept_event` that checks for the associated type of the field (of the `App.Event` union) which would need to be a `[]u8` or `[]u21` - // -> for the corresponding type generate the corresponding conversion calls to trigger the correct event automatically! + const event_type: enum { ascii, utf8 } = blk: { // check for type correctness and the associated type to use for the passed `accept_event` + const t = @FieldType(Event, @tagName(accept_event)); + const err_msg = "Unexpected type for the associated input completion event to trigger. Only `[]u8` or `[]u21` are allowed."; + switch (@typeInfo(t)) { + .pointer => |pointer| { + if (pointer.size != .slice) @compileError(err_msg); + switch (@typeInfo(pointer.child)) { + .int => |num| { + if (num.signedness != .unsigned) @compileError(err_msg); + switch (num.bits) { + 8 => break :blk .ascii, + 21 => break :blk .utf8, + else => @compileError(err_msg), + } + }, + else => @compileError(err_msg), + } + }, + else => @compileError(err_msg), + } + }; return struct { /// Offset from the end describing the current position of the cursor. cursor_offset: usize = 0, @@ -385,15 +405,10 @@ pub fn Input(Event: type, Queue: type) fn (accept_event: meta.FieldEnum(Event)) color: Color, pub fn init(color: Color) @This() { - return .{ - .color = color, - }; + return .{ .color = color }; } }; - // TODO make the event to trigger user defined (needs to be `comptime`) - // - can this even be agnostic to `u8` / `u21`? - pub fn init(allocator: std.mem.Allocator, queue: *Queue, configuration: Configuration) @This() { return .{ .configuration = configuration, @@ -505,11 +520,24 @@ pub fn Input(Event: type, Queue: type) fn (accept_event: meta.FieldEnum(Event)) // TODO enter to accept? // - shift+enter is not recognized by the input reader of `zterm`, so currently it is not possible to add newlines into the text box? if (key.eql(.{ .cp = input.Enter }) or key.eql(.{ .cp = input.KpEnter })) { - this.queue.push(@unionInit( - Event, - @tagName(accept_event), - try this.input.toOwnedSlice(), - )); + switch (event_type) { + .ascii => { + // NOTE convert unicode characters to ascii characters; if non ascii characters are found this is will fail! + var slice = try this.input.allocator.alloc(u8, this.input.items.len); + for (0.., this.input.items) |i, c| slice[i] = @intCast(c); + this.input.clearAndFree(); + this.queue.push(@unionInit( + Event, + @tagName(accept_event), + slice, + )); + }, + .utf8 => this.queue.push(@unionInit( + Event, + @tagName(accept_event), + try this.input.toOwnedSlice(), + )), + } this.cursor_offset = 0; } },