an experimental irc client

ui: query fg and bg colors, add blending

rockorager.dev 458eb42d a51b6646

verified
+51 -3
+2 -2
build.zig.zon
··· 7 7 .hash = "1220affeb3fe37ef09411b5a213b5fdf9bb6568e9913bade204694648983a8b2776d", 8 8 }, 9 9 .vaxis = .{ 10 - .url = "git+https://github.com/rockorager/libvaxis#92657ef00ef908c3b79faacfa14b9a7490a4a7f1", 11 - .hash = "1220bf55a8d117f93f989373fce3439137471bc4e4fc4ec77aa2614c04a2b251e7a1", 10 + .url = "git+https://github.com/rockorager/libvaxis#b6043f4497cea176acaea5574ff7011383f1c706", 11 + .hash = "1220d1ad3e0cd20b4a7c0810aec229a1af0a95c80bcdb4acbe69d04db4e5c0f403d3", 12 12 }, 13 13 .zeit = .{ 14 14 .url = "git+https://github.com/rockorager/zeit?ref=main#d943bc4bfe9e18490460dfdd64f48e997065eba8",
+48
src/app.zig
··· 82 82 /// Whether the application has focus or not 83 83 has_focus: bool, 84 84 85 + fg: ?[3]u8, 86 + bg: ?[3]u8, 87 + 85 88 const default_rhs: vxfw.Text = .{ .text = "TODO: update this text" }; 86 89 87 90 /// initialize vaxis, lua state ··· 121 124 .ctx = null, 122 125 .last_height = 0, 123 126 .has_focus = true, 127 + .fg = null, 128 + .bg = null, 124 129 }; 125 130 126 131 self.lua = try Lua.init(&self.alloc); ··· 203 208 // callbacks 204 209 self.ctx = ctx; 205 210 switch (event) { 211 + .color_report => |color| { 212 + switch (color.kind) { 213 + .fg => self.fg = color.value, 214 + .bg => self.bg = color.value, 215 + else => {}, 216 + } 217 + if (self.fg != null and self.bg != null) { 218 + for (self.clients.items) |client| { 219 + for (client.channels.items) |channel| { 220 + channel.text_field.style.bg = self.blend(10); 221 + } 222 + } 223 + } 224 + }, 206 225 .key_press => |key| { 207 226 if (self.state.paste.pasting) { 208 227 ctx.consume_event = true; ··· 267 286 const title = try std.fmt.bufPrint(&self.title_buf, "comlink", .{}); 268 287 try ctx.setTitle(title); 269 288 try ctx.tick(8, self.widget()); 289 + try ctx.queryColor(.fg); 290 + try ctx.queryColor(.bg); 270 291 }, 271 292 .tick => { 272 293 for (self.clients.items) |client| { ··· 386 407 i += 1; 387 408 } 388 409 } 410 + } 411 + 412 + /// Blend fg and bg, otherwise return index 8. amt will be clamped to [0,100]. amt will be 413 + /// interpreted as percentage of fg to blend into bg 414 + pub fn blend(self: *App, amt: u8) vaxis.Color { 415 + const bg = self.bg orelse return .{ .index = 8 }; 416 + const fg = self.fg orelse return .{ .index = 8 }; 417 + // Clamp to (0,100) 418 + if (amt == 0) return .{ .rgb = bg }; 419 + if (amt >= 100) return .{ .rgb = fg }; 420 + 421 + const fg_r: u16 = std.math.mulWide(u8, fg[0], amt); 422 + const fg_g: u16 = std.math.mulWide(u8, fg[1], amt); 423 + const fg_b: u16 = std.math.mulWide(u8, fg[2], amt); 424 + 425 + const bg_multiplier: u8 = 100 - amt; 426 + const bg_r: u16 = std.math.mulWide(u8, bg[0], bg_multiplier); 427 + const bg_g: u16 = std.math.mulWide(u8, bg[1], bg_multiplier); 428 + const bg_b: u16 = std.math.mulWide(u8, bg[2], bg_multiplier); 429 + 430 + return .{ 431 + .rgb = .{ 432 + @intCast((fg_r + bg_r) / 100), 433 + @intCast((fg_g + bg_g) / 100), 434 + @intCast((fg_b + bg_b) / 100), 435 + }, 436 + }; 389 437 } 390 438 391 439 /// handle a command
+1 -1
src/irc.zig
··· 280 280 .completer = Completer.init(gpa), 281 281 }; 282 282 283 - self.text_field.style = .{ .bg = .{ .index = 8 } }; 283 + self.text_field.style = .{ .bg = client.app.blend(10) }; 284 284 self.text_field.userdata = self; 285 285 self.text_field.onSubmit = Channel.onSubmit; 286 286 self.text_field.onChange = Channel.onChange;