this repo has no description
3
fork

Configure Feed

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

ui: handle mouse hover

+83 -6
+2 -2
build.zig.zon
··· 7 7 .hash = "1220affeb3fe37ef09411b5a213b5fdf9bb6568e9913bade204694648983a8b2776d", 8 8 }, 9 9 .vaxis = .{ 10 - .url = "git+https://github.com/rockorager/libvaxis#0fb96df48ede1823107f9ea7dc9b4dc8f8399d9d", 11 - .hash = "1220f03c86f6352a1e9ee24529e7c031b0bbe802145e5fe1b83beb7c524bfcaa02ba", 10 + .url = "git+https://github.com/rockorager/libvaxis#01e7b6644b63ca3883ca43a509fcee62da18521e", 11 + .hash = "1220f5d235ab148bc7c0bdf3870271145d22cd35fd6745047e66a019d98e2cd43020", 12 12 }, 13 13 .zeit = .{ 14 14 .url = "git+https://github.com/rockorager/zeit?ref=main#d943bc4bfe9e18490460dfdd64f48e997065eba8",
+81 -4
src/irc.zig
··· 135 135 pending: i16 = 0, 136 136 } = .{}, 137 137 138 + message_view: struct { 139 + mouse: ?vaxis.Mouse = null, 140 + hovered_message: ?Message = null, 141 + } = .{}, 142 + 143 + // Gutter (left side where time is printed) width 144 + const gutter_width = 6; 145 + 138 146 pub const Member = struct { 139 147 user: *User, 140 148 ··· 447 455 const self: *Channel = @ptrCast(@alignCast(ptr)); 448 456 switch (event) { 449 457 .mouse => |mouse| { 458 + if (self.message_view.mouse) |last_mouse| { 459 + // We need to redraw if the column entered the gutter 460 + if (last_mouse.col >= gutter_width and mouse.col < gutter_width) 461 + ctx.redraw = true 462 + // Or if the column exited the gutter 463 + else if (last_mouse.col < gutter_width and mouse.col >= gutter_width) 464 + ctx.redraw = true 465 + // Or if the row changed 466 + else if (last_mouse.row != mouse.row) 467 + ctx.redraw = true 468 + // Or if we did a middle click, and now released it 469 + else if (last_mouse.button == .middle) 470 + ctx.redraw = true; 471 + } 472 + 473 + // Save this mouse state for when we draw 474 + self.message_view.mouse = mouse; 475 + 476 + if (mouse.type == .press and 477 + mouse.button == .middle and 478 + self.message_view.hovered_message != null) 479 + { 480 + const msg = self.message_view.hovered_message orelse unreachable; 481 + var iter = msg.paramIterator(); 482 + // Skip the target 483 + _ = iter.next() orelse unreachable; 484 + // Get the content 485 + const content = iter.next() orelse unreachable; 486 + try ctx.copyToClipboard(content); 487 + return ctx.consumeAndRedraw(); 488 + } 450 489 if (mouse.button == .wheel_down) { 451 490 self.scroll.pending -|= 3; 452 491 ctx.consume_event = true; ··· 458 497 if (self.scroll.pending != 0) { 459 498 return self.doScroll(ctx); 460 499 } 500 + }, 501 + .mouse_leave => { 502 + self.message_view.mouse = null; 503 + self.message_view.hovered_message = null; 504 + ctx.redraw = true; 461 505 }, 462 506 .tick => try self.doScroll(ctx), 463 507 else => {}, ··· 544 588 var row: i17 = max.height + self.scroll.offset; 545 589 // Message offset 546 590 const offset = self.scroll.msg_offset orelse self.messages.items.len; 547 - // Timezone ref 548 - // Gutter (left side where time is printed) width 549 - const gutter_width = 6; 550 591 551 592 const messages = self.messages.items[0..offset]; 552 593 var iter = std.mem.reverseIterator(messages); ··· 604 645 // Draw the message so we have it's wrapped height 605 646 const text: vxfw.Text = .{ .text = content }; 606 647 const child_ctx = ctx.withConstraints( 607 - .{ .height = 0, .width = 0 }, 648 + .{ .width = 0, .height = 0 }, 608 649 .{ .width = max.width -| gutter_width, .height = null }, 609 650 ); 610 651 const surface = try text.draw(child_ctx); 652 + 653 + // See if our message contains the mouse. We'll highlight it if it does 654 + const message_has_mouse: bool = blk: { 655 + const mouse = self.message_view.mouse orelse break :blk false; 656 + break :blk mouse.col >= gutter_width and 657 + mouse.row < row and 658 + mouse.row >= row - surface.size.height; 659 + }; 660 + 661 + if (message_has_mouse) { 662 + const last_mouse = self.message_view.mouse orelse unreachable; 663 + // If we had a middle click, we highlight yellow to indicate we copied the text 664 + const bg: vaxis.Color = if (last_mouse.button == .middle and last_mouse.type == .press) 665 + .{ .index = 3 } 666 + else 667 + .{ .index = 8 }; 668 + // Set the style for the entire message 669 + for (surface.buffer) |*cell| { 670 + cell.style.bg = bg; 671 + } 672 + // Create a surface to highlight the entire area under the message 673 + const hl_surface = try vxfw.Surface.init( 674 + ctx.arena, 675 + text.widget(), 676 + .{ .width = max.width -| gutter_width, .height = surface.size.height }, 677 + ); 678 + const base: vaxis.Cell = .{ .style = .{ .bg = bg } }; 679 + @memset(hl_surface.buffer, base); 680 + 681 + try children.append(.{ 682 + .origin = .{ .row = row - surface.size.height, .col = gutter_width }, 683 + .surface = hl_surface, 684 + }); 685 + 686 + self.message_view.hovered_message = msg; 687 + } 611 688 612 689 // Adjust the row we print on for the wrapped height of this message 613 690 row -= surface.size.height;