an experimental irc client

ui: anchor message view when we have scroll

+18 -4
+18 -4
src/irc.zig
··· 128 128 offset: u16 = 0, 129 129 /// Message offset into the list of messages. We use this to lock the viewport if we have a 130 130 /// scroll. Otherwise, when offset == 0 this is effectively ignored (and should be 0) 131 - msg_offset: u32 = 0, 131 + msg_offset: ?u16 = null, 132 132 133 133 /// Pending scroll we have to handle while drawing. This could be up or down. By convention 134 134 /// we say positive is a scroll up. 135 - pending: i17 = 0, 135 + pending: i16 = 0, 136 136 } = .{}, 137 137 138 138 pub const Member = struct { ··· 466 466 467 467 /// Consumes any pending scrolls and schedules another tick if needed 468 468 fn doScroll(self: *Channel, ctx: *vxfw.EventContext) anyerror!void { 469 + defer { 470 + // At the end of this function, we anchor our msg_offset if we have any amount of 471 + // scroll. This prevents new messages from automatically scrolling us 472 + if (self.scroll.offset > 0 and self.scroll.msg_offset == null) { 473 + self.scroll.msg_offset = @intCast(self.messages.items.len); 474 + } 475 + // If we have no offset, we reset our anchor 476 + if (self.scroll.offset == 0) { 477 + self.scroll.msg_offset = null; 478 + } 479 + } 469 480 const animation_tick: u32 = 30; 470 481 // No pending scroll. Return early 471 482 if (self.scroll.pending == 0) return; ··· 526 537 527 538 var children = std.ArrayList(vxfw.SubSurface).init(ctx.arena); 528 539 529 - // Row is the row we are printing on. 540 + // Row is the row we are printing on. We add the offset to achieve our scroll location 530 541 var row: i17 = max.height + self.scroll.offset; 531 - var iter = std.mem.reverseIterator(self.messages.items); 542 + 543 + const offset = self.scroll.msg_offset orelse self.messages.items.len; 544 + 545 + var iter = std.mem.reverseIterator(self.messages.items[0..offset]); 532 546 const gutter_width = 6; 533 547 while (iter.next()) |msg| { 534 548 // Break if we have gone past the top of the screen