this repo has no description
13
fork

Configure Feed

Select the types of activity you want to include in your feed.

widgets: create an initial text_input and border widget

Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>

+170 -2
+1 -1
build.zig
··· 14 14 15 15 const exe = b.addExecutable(.{ 16 16 .name = "vaxis", 17 - .root_source_file = .{ .path = "examples/main.zig" }, 17 + .root_source_file = .{ .path = "examples/text_input.zig" }, 18 18 .target = target, 19 19 .optimize = optimize, 20 20 });
+79
examples/text_input.zig
··· 1 + const std = @import("std"); 2 + const vaxis = @import("vaxis"); 3 + const Cell = vaxis.Cell; 4 + const TextInput = vaxis.widgets.TextInput; 5 + const border = vaxis.widgets.border; 6 + 7 + const log = std.log.scoped(.main); 8 + pub fn main() !void { 9 + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; 10 + defer { 11 + const deinit_status = gpa.deinit(); 12 + //fail test; can't try in defer as defer is executed after we return 13 + if (deinit_status == .leak) { 14 + log.err("memory leak", .{}); 15 + } 16 + } 17 + const alloc = gpa.allocator(); 18 + 19 + // Initialize Vaxis 20 + var vx = try vaxis.init(Event, .{}); 21 + defer vx.deinit(alloc); 22 + 23 + // Start the read loop. This puts the terminal in raw mode and begins 24 + // reading user input 25 + try vx.start(); 26 + defer vx.stop(); 27 + 28 + // Optionally enter the alternate screen 29 + try vx.enterAltScreen(); 30 + 31 + var text_input: TextInput = .{}; 32 + 33 + // The main event loop. Vaxis provides a thread safe, blocking, buffered 34 + // queue which can serve as the primary event queue for an application 35 + outer: while (true) { 36 + // nextEvent blocks until an event is in the queue 37 + const event = vx.nextEvent(); 38 + log.debug("event: {}\r\n", .{event}); 39 + // exhaustive switching ftw. Vaxis will send events if your EventType 40 + // enum has the fields for those events (ie "key_press", "winsize") 41 + switch (event) { 42 + .key_press => |key| { 43 + text_input.update(.{ .key_press = key }); 44 + if (key.codepoint == 'c' and key.mods.ctrl) { 45 + break :outer; 46 + } 47 + }, 48 + .winsize => |ws| { 49 + try vx.resize(alloc, ws); 50 + }, 51 + else => {}, 52 + } 53 + 54 + // vx.window() returns the root window. This window is the size of the 55 + // terminal and can spawn child windows as logical areas. Child windows 56 + // cannot draw outside of their bounds 57 + const win = vx.window(); 58 + // Clear the entire space because we are drawing in immediate mode. 59 + // vaxis double buffers the screen. This new frame will be compared to 60 + // the old and only updated cells will be drawn 61 + win.clear(); 62 + const child = win.initChild(win.width / 2 - 20, win.height / 2 - 3, .{ .limit = 40 }, .{ .limit = 3 }); 63 + // draw the text_input using a bordered window 64 + text_input.draw(border.all(child, .{})); 65 + 66 + // Render the screen 67 + try vx.render(); 68 + } 69 + } 70 + 71 + // Our EventType. This can contain internal events as well as Vaxis events. 72 + // Internal events can be posted into the same queue as vaxis events to allow 73 + // for a single event loop with exhaustive switching. Booya 74 + const Event = union(enum) { 75 + key_press: vaxis.Key, 76 + winsize: vaxis.Winsize, 77 + focus_in, 78 + foo: u8, 79 + };
+3 -1
src/Key.zig
··· 14 14 /// the unicode codepoint of the key event. 15 15 codepoint: u21, 16 16 17 - /// the text generated from the key event, if any 17 + /// the text generated from the key event. This will only contain a value if the 18 + /// event generated a multi-codepoint grapheme. If there was only a single 19 + /// codepoint, library users can encode the codepoint directly 18 20 text: ?[]const u8 = null, 19 21 20 22 /// the shifted codepoint of this key event. This will only be present if the
+2
src/main.zig
··· 7 7 pub const Key = @import("Key.zig"); 8 8 pub const Winsize = @import("Tty.zig").Winsize; 9 9 10 + pub const widgets = @import("widgets/main.zig"); 11 + 10 12 /// Initialize a Vaxis application. 11 13 pub fn init(comptime EventType: type, opts: Options) !Vaxis(EventType) { 12 14 return Vaxis(EventType).init(opts);
+52
src/widgets/TextInput.zig
··· 1 + const std = @import("std"); 2 + const Cell = @import("../cell.zig").Cell; 3 + const Key = @import("../Key.zig"); 4 + const Window = @import("../Window.zig"); 5 + 6 + const log = std.log.scoped(.text_input); 7 + 8 + const TextInput = @This(); 9 + 10 + /// The events that this widget handles 11 + const Event = union(enum) { 12 + key_press: Key, 13 + }; 14 + 15 + // Index of our cursor 16 + cursor_idx: usize = 0, 17 + 18 + // the actual line of input 19 + buffer: [4096]u8 = undefined, 20 + buffer_idx: usize = 0, 21 + 22 + pub fn update(self: *TextInput, event: Event) void { 23 + switch (event) { 24 + .key_press => |key| { 25 + switch (key.codepoint) { 26 + 0x20...0x7E => { 27 + self.buffer[self.buffer_idx] = @truncate(key.codepoint); 28 + self.buffer_idx += 1; 29 + self.cursor_idx += 1; 30 + }, 31 + Key.backspace => { 32 + // TODO: this only works at the end of the array. Then 33 + // again, we don't have any means to move the cursor yet 34 + if (self.buffer_idx == 0) return; 35 + self.buffer_idx -= 1; 36 + }, 37 + else => {}, 38 + } 39 + }, 40 + } 41 + } 42 + 43 + pub fn draw(self: *TextInput, win: Window) void { 44 + for (0.., self.buffer[0..self.buffer_idx]) |i, b| { 45 + win.writeCell(i, 0, .{ 46 + .char = .{ 47 + .grapheme = &[_]u8{b}, 48 + .width = 1, 49 + }, 50 + }); 51 + } 52 + }
+31
src/widgets/border.zig
··· 1 + const Window = @import("../Window.zig"); 2 + const cell = @import("../cell.zig"); 3 + const Character = cell.Character; 4 + const Style = cell.Style; 5 + 6 + const horizontal = Character{ .grapheme = "─", .width = 1 }; 7 + const vertical = Character{ .grapheme = "│", .width = 1 }; 8 + const top_left = Character{ .grapheme = "╭", .width = 1 }; 9 + const top_right = Character{ .grapheme = "╮", .width = 1 }; 10 + const bottom_right = Character{ .grapheme = "╯", .width = 1 }; 11 + const bottom_left = Character{ .grapheme = "╰", .width = 1 }; 12 + 13 + pub fn all(win: Window, style: Style) Window { 14 + const h = win.height; 15 + const w = win.width; 16 + win.writeCell(0, 0, .{ .char = top_left, .style = style }); 17 + win.writeCell(0, h - 1, .{ .char = bottom_left, .style = style }); 18 + win.writeCell(w - 1, 0, .{ .char = top_right, .style = style }); 19 + win.writeCell(w - 1, h - 1, .{ .char = bottom_right, .style = style }); 20 + var i: usize = 1; 21 + while (i < (h - 1)) : (i += 1) { 22 + win.writeCell(0, i, .{ .char = vertical, .style = style }); 23 + win.writeCell(w - 1, i, .{ .char = vertical, .style = style }); 24 + } 25 + i = 1; 26 + while (i < w - 1) : (i += 1) { 27 + win.writeCell(i, 0, .{ .char = horizontal, .style = style }); 28 + win.writeCell(i, h - 1, .{ .char = horizontal, .style = style }); 29 + } 30 + return win.initChild(1, 1, .{ .limit = w - 2 }, .{ .limit = w - 2 }); 31 + }
+2
src/widgets/main.zig
··· 1 + pub const TextInput = @import("TextInput.zig"); 2 + pub const border = @import("border.zig");