an experimental irc client
at main 2.3 kB view raw
1const std = @import("std"); 2const vaxis = @import("vaxis"); 3const vxfw = vaxis.vxfw; 4 5const Scrollbar = @This(); 6 7/// character to use for the scrollbar 8const character: vaxis.Cell.Character = .{ .grapheme = "", .width = 1 }; 9const empty: vaxis.Cell = .{ .char = character, .style = .{ .fg = .{ .index = 8 } } }; 10 11/// style to draw the bar character with 12style: vaxis.Style = .{}, 13 14/// The index of the bottom-most item, with 0 being "at the bottom" 15bottom: u16 = 0, 16 17/// total items in the list 18total: u16, 19 20/// total items that fit within the view area 21view_size: u16, 22 23fn widget(self: *Scrollbar) vxfw.Widget { 24 return .{ 25 .userdata = self, 26 .drawFn = drawFn, 27 }; 28} 29 30fn drawFn(ptr: *anyopaque, ctx: vxfw.DrawContext) std.mem.Allocator.Error!vxfw.Surface { 31 const self: *Scrollbar = @ptrCast(@alignCast(ptr)); 32 return self.draw(ctx); 33} 34 35pub fn draw(self: *Scrollbar, ctx: vxfw.DrawContext) std.mem.Allocator.Error!vxfw.Surface { 36 const max = ctx.max.size(); 37 if (max.width == 0 or max.height == 0) { 38 return .{ 39 .size = .{ .width = 0, .height = 0 }, 40 .widget = self.widget(), 41 .buffer = &.{}, 42 .children = &.{}, 43 }; 44 } 45 46 const surface = try vxfw.Surface.init( 47 ctx.arena, 48 self.widget(), 49 .{ .width = 2, .height = max.height }, 50 ); 51 52 // don't draw when 0 items 53 if (self.total < 1) return surface; 54 55 // don't draw when all items can be shown 56 if (self.view_size >= self.total) return surface; 57 58 @memset(surface.buffer, empty); 59 60 // (view_size / total) * window height = size of the scroll bar 61 const premul1 = std.math.mulWide(u16, self.view_size, max.height); 62 const bar_height = @max(std.math.divCeil(usize, premul1, self.total) catch unreachable, 1); 63 64 // Premultiply. We use mulWide to ensure we never overflow 65 const premul2 = std.math.mulWide(u16, self.bottom, max.height); 66 // The row of the last cell of the bottom of the bar 67 const bar_bottom = (max.height - 1) -| (std.math.divCeil(usize, premul2, self.total) catch unreachable); 68 69 var i: usize = 0; 70 while (i <= bar_height) : (i += 1) 71 surface.writeCell(0, @intCast(bar_bottom -| i), .{ .char = character, .style = self.style }); 72 73 return surface; 74}