an experimental irc client

ui: handle paste and message limit indicator

+61 -7
+2 -2
build.zig.zon
··· 7 .hash = "1220affeb3fe37ef09411b5a213b5fdf9bb6568e9913bade204694648983a8b2776d", 8 }, 9 .vaxis = .{ 10 - .url = "git+https://github.com/rockorager/libvaxis#af450ebb1ba068c5d44d7c878fa5157188094c0a", 11 - .hash = "1220212b2aa3c0148526494403917994acc01aec1f123634df5fe843e7f034c0b703", 12 }, 13 .zeit = .{ 14 .url = "git+https://github.com/rockorager/zeit?ref=main#d943bc4bfe9e18490460dfdd64f48e997065eba8",
··· 7 .hash = "1220affeb3fe37ef09411b5a213b5fdf9bb6568e9913bade204694648983a8b2776d", 8 }, 9 .vaxis = .{ 10 + .url = "git+https://github.com/rockorager/libvaxis#bcc1d027cb2ede571bb76669441d2e09944bd3d3", 11 + .hash = "1220fca568653885767394bc659714e0e9ccda935e5c02d4333b1d60b064bc6ac0bc", 12 }, 13 .zeit = .{ 14 .url = "git+https://github.com/rockorager/zeit?ref=main#d943bc4bfe9e18490460dfdd64f48e997065eba8",
+27
src/app.zig
··· 198 self.ctx = ctx; 199 switch (event) { 200 .key_press => |key| { 201 if (key.matches('c', .{ .ctrl = true })) { 202 ctx.quit = true; 203 } ··· 212 else => {}, 213 } 214 return ctx.consumeAndRedraw(); 215 } 216 } 217 },
··· 198 self.ctx = ctx; 199 switch (event) { 200 .key_press => |key| { 201 + if (self.state.paste.pasting) { 202 + ctx.consume_event = true; 203 + // Always ignore enter key 204 + if (key.codepoint == vaxis.Key.enter) return; 205 + if (key.text) |text| { 206 + try self.paste_buffer.appendSlice(text); 207 + } 208 + return; 209 + } 210 if (key.matches('c', .{ .ctrl = true })) { 211 ctx.quit = true; 212 } ··· 221 else => {}, 222 } 223 return ctx.consumeAndRedraw(); 224 + } 225 + } 226 + }, 227 + .paste_start => self.state.paste.pasting = true, 228 + .paste_end => { 229 + self.state.paste.pasting = false; 230 + if (std.mem.indexOfScalar(u8, self.paste_buffer.items, '\n')) |_| { 231 + log.debug("paste had line ending", .{}); 232 + return; 233 + } 234 + defer self.paste_buffer.clearRetainingCapacity(); 235 + if (self.selectedBuffer()) |buffer| { 236 + switch (buffer) { 237 + .client => {}, 238 + .channel => |channel| { 239 + try channel.text_field.insertSliceAtCursor(self.paste_buffer.items); 240 + return ctx.consumeAndRedraw(); 241 + }, 242 } 243 } 244 },
+32 -5
src/irc.zig
··· 620 .surface = scrollbar_surface, 621 }); 622 623 - // Draw the text field 624 - try children.append(.{ 625 - .origin = .{ .col = 0, .row = max.height - 1 }, 626 - .surface = try self.text_field.draw(ctx), 627 - }); 628 629 if (self.completer_shown) { 630 const widest: u16 = @intCast(self.completer.widestMatch(ctx));
··· 620 .surface = scrollbar_surface, 621 }); 622 623 + { 624 + // Draw the character limit. 14 is length of message overhead "PRIVMSG :\r\n" 625 + const max_limit = maximum_message_size -| self.name.len -| 14 -| self.name.len; 626 + const limit = try std.fmt.allocPrint( 627 + ctx.arena, 628 + "{d}/{d}", 629 + .{ self.text_field.buf.realLength(), max_limit }, 630 + ); 631 + const style: vaxis.Style = if (self.text_field.buf.realLength() > max_limit) 632 + .{ .fg = .{ .index = 1 } } 633 + else 634 + .{ .dim = true }; 635 + const limit_text: vxfw.Text = .{ .text = limit, .style = style }; 636 + const limit_ctx = ctx.withConstraints(.{}, ctx.max); 637 + const limit_s = try limit_text.draw(limit_ctx); 638 + 639 + try children.append(.{ 640 + .origin = .{ .col = max.width -| limit_s.size.width, .row = max.height - 1 }, 641 + .surface = limit_s, 642 + }); 643 + 644 + const text_field_ctx = ctx.withConstraints( 645 + ctx.min, 646 + .{ .height = 1, .width = max.width -| limit_s.size.width -| 1 }, 647 + ); 648 + 649 + // Draw the text field 650 + try children.append(.{ 651 + .origin = .{ .col = 0, .row = max.height - 1 }, 652 + .surface = try self.text_field.draw(text_field_ctx), 653 + }); 654 + } 655 656 if (self.completer_shown) { 657 const widest: u16 = @intCast(self.completer.widestMatch(ctx));