diff --git a/src/app.zig b/src/app.zig index 98f47b7..da9110a 100644 --- a/src/app.zig +++ b/src/app.zig @@ -296,7 +296,6 @@ pub fn App(comptime E: type, comptime R: fn (comptime bool) type, comptime fulls }; this.postEvent(.{ .key = key }); }, - 'I' => this.postEvent(.{ .focus = true }), 'O' => this.postEvent(.{ .focus = false }), // 'M', 'm' => return parseMouse(sequence), // TODO: parse mouse inputs diff --git a/src/widget.zig b/src/widget.zig index 7534220..8a95cd7 100644 --- a/src/widget.zig +++ b/src/widget.zig @@ -91,6 +91,7 @@ pub fn Widget(comptime Event: type, comptime Renderer: type) type { // TODO: implement a minimal size requirement for Widgets to render correctly? // import and export of `Widget` implementations + pub const Input = @import("widget/Input.zig").Widget(Event, Renderer); pub const Text = @import("widget/Text.zig").Widget(Event, Renderer); pub const RawText = @import("widget/RawText.zig").Widget(Event, Renderer); pub const Spacer = @import("widget/Spacer.zig").Widget(Event, Renderer); @@ -98,6 +99,7 @@ pub fn Widget(comptime Event: type, comptime Renderer: type) type { }; // test widget implementation satisfies the interface comptime Type.Interface.satisfiedBy(Type); + comptime Type.Interface.satisfiedBy(Type.Input); comptime Type.Interface.satisfiedBy(Type.Text); comptime Type.Interface.satisfiedBy(Type.RawText); comptime Type.Interface.satisfiedBy(Type.Spacer); diff --git a/src/widget/Input.zig b/src/widget/Input.zig new file mode 100644 index 0000000..82be21d --- /dev/null +++ b/src/widget/Input.zig @@ -0,0 +1,242 @@ +const std = @import("std"); +const terminal = @import("../terminal.zig"); + +const isTaggedUnion = @import("../event.zig").isTaggedUnion; +const Cell = terminal.Cell; +const Key = terminal.Key; +const Size = terminal.Size; + +const log = std.log.scoped(.widget_input); + +pub fn Widget(comptime Event: type, comptime Renderer: type) type { + if (!isTaggedUnion(Event)) { + @compileError("Provided user event `Event` for `Layout(comptime Event: type)` is not of type `union(enum)`."); + } + return struct { + active: bool = false, + allocator: std.mem.Allocator = undefined, + label: ?[]const u8 = null, + placeholder: ?[]const u8 = null, + size: Size = undefined, + require_render: bool = false, + value: std.ArrayList(u8) = undefined, + value_len: usize = 0, // value content length + cursor_idx: usize = 0, // current cursor position + + pub fn init(allocator: std.mem.Allocator, label: ?[]const u8, placeholder: ?[]const u8) @This() { + var value = std.ArrayList(u8).init(allocator); + value.resize(32) catch @panic("Input.zig: out of memory"); + return .{ + .allocator = allocator, + .value = value, + .label = label, + .placeholder = placeholder, + }; + } + + pub fn deinit(this: *@This()) void { + this.value.deinit(); + this.* = undefined; + } + + pub fn getValue(this: *const @This()) []const u8 { + return this.value.items[0..this.value_len]; + } + + pub fn handle(this: *@This(), event: Event) ?Event { + switch (event) { + .resize => |size| { + this.size = size; + this.require_render = true; + + var required_cols: u16 = 4; // '...c' + if (this.label) |label| { + required_cols += @as(u16, @truncate(label.len)); //