tangled
alpha
login
or
join now
rockorager.dev
/
comlink
2
fork
atom
an experimental irc client
2
fork
atom
overview
issues
pulls
pipelines
scroll: improve animation
rockorager.dev
1 year ago
123e08d3
538115cd
verified
This commit was signed with the committer's
known signature
.
rockorager.dev
SSH Key Fingerprint:
SHA256:qn/Fjy7CpbcogGEPB14Y53hLnQleZNFY9lkQnuudFLs=
+76
-7
1 changed file
expand all
collapse all
unified
split
src
irc.zig
+76
-7
src/irc.zig
···
164
pending: i17 = 0,
165
} = .{},
166
0
0
167
message_view: struct {
168
mouse: ?vaxis.Mouse = null,
169
hovered_message: ?Message = null,
···
589
}
590
if (key.matches(vaxis.Key.page_up, .{})) {
591
self.scroll.pending += self.client.app.last_height / 2;
0
592
try self.doScroll(ctx);
593
return ctx.consumeAndRedraw();
594
}
595
if (key.matches(vaxis.Key.page_down, .{})) {
0
596
self.scroll.pending -|= self.client.app.last_height / 2;
597
try self.doScroll(ctx);
598
return ctx.consumeAndRedraw();
599
}
600
if (key.matches(vaxis.Key.home, .{})) {
0
601
self.scroll.pending -= self.scroll.offset;
602
self.scroll.msg_offset = null;
603
try self.doScroll(ctx);
···
868
self.scroll.msg_offset = null;
869
}
870
}
871
-
const animation_tick: u32 = 30;
872
// No pending scroll. Return early
873
if (self.scroll.pending == 0) return;
0
0
0
874
875
// Scroll up
876
if (self.scroll.pending > 0) {
···
879
self.scroll.pending = 0;
880
return;
881
}
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
882
// Consume 1 line, and schedule a tick
883
-
self.scroll.offset += 1;
884
-
self.scroll.pending -= 1;
885
ctx.redraw = true;
886
return ctx.tick(animation_tick, self.messageViewWidget());
887
}
···
896
897
// Scroll down
898
if (self.scroll.pending < 0) {
899
-
// Consume 1 line, and schedule a tick
900
-
self.scroll.offset -= 1;
901
-
self.scroll.pending += 1;
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
902
ctx.redraw = true;
903
return ctx.tick(animation_tick, self.messageViewWidget());
904
}
···
2727
pub fn connect(self: *Client) !void {
2728
if (self.config.tls) {
2729
const port: u16 = self.config.port orelse 6697;
2730
-
self.stream = try std.net.tcpConnectToHost(self.alloc, self.config.server, port);
2731
self.client = try tls.client(self.stream, .{
2732
.host = self.config.server,
2733
.root_ca = .{ .bundle = self.app.bundle },
···
3830
};
3831
}
3832
};
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
3833
3834
test "caseFold" {
3835
try testing.expect(caseFold("a", "A"));
···
164
pending: i17 = 0,
165
} = .{},
166
167
+
animation_end_ms: u64 = 0,
168
+
169
message_view: struct {
170
mouse: ?vaxis.Mouse = null,
171
hovered_message: ?Message = null,
···
591
}
592
if (key.matches(vaxis.Key.page_up, .{})) {
593
self.scroll.pending += self.client.app.last_height / 2;
594
+
self.animation_end_ms = @intCast(std.time.milliTimestamp() + 200);
595
try self.doScroll(ctx);
596
return ctx.consumeAndRedraw();
597
}
598
if (key.matches(vaxis.Key.page_down, .{})) {
599
+
self.animation_end_ms = @intCast(std.time.milliTimestamp() + 200);
600
self.scroll.pending -|= self.client.app.last_height / 2;
601
try self.doScroll(ctx);
602
return ctx.consumeAndRedraw();
603
}
604
if (key.matches(vaxis.Key.home, .{})) {
605
+
self.animation_end_ms = @intCast(std.time.milliTimestamp() + 200);
606
self.scroll.pending -= self.scroll.offset;
607
self.scroll.msg_offset = null;
608
try self.doScroll(ctx);
···
873
self.scroll.msg_offset = null;
874
}
875
}
0
876
// No pending scroll. Return early
877
if (self.scroll.pending == 0) return;
878
+
879
+
const animation_tick: u32 = 8;
880
+
const now_ms: u64 = @intCast(std.time.milliTimestamp());
881
882
// Scroll up
883
if (self.scroll.pending > 0) {
···
886
self.scroll.pending = 0;
887
return;
888
}
889
+
890
+
// At this point, we always redraw
891
+
ctx.redraw = true;
892
+
893
+
// If we are past the end of the animation, or on the last tick, consume the rest of the
894
+
// pending scroll
895
+
if (self.animation_end_ms <= now_ms) {
896
+
self.scroll.offset += @intCast(self.scroll.pending);
897
+
self.scroll.pending = 0;
898
+
return;
899
+
}
900
+
901
+
// Calculate the amount to scroll this tick. We use 8ms ticks.
902
+
// Total time = end_ms - now_ms
903
+
// Lines / ms = self.scroll.pending / total time
904
+
// Lines this tick = 8 ms * lines / ms
905
+
// All together: (8 ms * self.scroll.pending ) / (end_ms - now_ms)
906
+
const delta_scroll = (@as(u64, animation_tick) * @as(u64, @intCast(self.scroll.pending))) /
907
+
(self.animation_end_ms - now_ms);
908
+
909
+
// Ensure we always scroll at least one line
910
+
const resolved_scroll = @max(1, delta_scroll);
911
+
912
// Consume 1 line, and schedule a tick
913
+
self.scroll.offset += @intCast(resolved_scroll);
914
+
self.scroll.pending -|= @intCast(resolved_scroll);
915
ctx.redraw = true;
916
return ctx.tick(animation_tick, self.messageViewWidget());
917
}
···
926
927
// Scroll down
928
if (self.scroll.pending < 0) {
929
+
const pending: u16 = @intCast(@abs(self.scroll.pending));
930
+
931
+
// At this point, we always redraw
932
+
ctx.redraw = true;
933
+
934
+
// If we are past the end of the animation, or on the last tick, consume the rest of the
935
+
// pending scroll
936
+
if (self.animation_end_ms <= now_ms) {
937
+
self.scroll.offset -|= pending;
938
+
self.scroll.pending = 0;
939
+
return;
940
+
}
941
+
942
+
// Calculate the amount to scroll this tick. We use 8ms ticks.
943
+
// Total time = end_ms - now_ms
944
+
// Lines / ms = self.scroll.pending / total time
945
+
// Lines this tick = 8 ms * lines / ms
946
+
// All together: (8 ms * self.scroll.pending ) / (end_ms - now_ms)
947
+
const delta_scroll = (@as(u64, animation_tick) * @as(u64, @intCast(pending))) /
948
+
(self.animation_end_ms - now_ms);
949
+
950
+
// Ensure we always scroll at least one line
951
+
const resolved_scroll = @max(1, delta_scroll);
952
+
self.scroll.offset -|= @intCast(resolved_scroll);
953
+
self.scroll.pending += @intCast(resolved_scroll);
954
ctx.redraw = true;
955
return ctx.tick(animation_tick, self.messageViewWidget());
956
}
···
2779
pub fn connect(self: *Client) !void {
2780
if (self.config.tls) {
2781
const port: u16 = self.config.port orelse 6697;
2782
+
self.stream = try tcpConnectToHost(self.alloc, self.config.server, port);
2783
self.client = try tls.client(self.stream, .{
2784
.host = self.config.server,
2785
.root_ca = .{ .bundle = self.app.bundle },
···
3882
};
3883
}
3884
};
3885
+
3886
+
/// All memory allocated with `allocator` will be freed before this function returns.
3887
+
pub fn tcpConnectToHost(allocator: mem.Allocator, name: []const u8, port: u16) std.net.TcpConnectToHostError!std.net.Stream {
3888
+
const list = try std.net.getAddressList(allocator, name, port);
3889
+
defer list.deinit();
3890
+
3891
+
if (list.addrs.len == 0) return error.UnknownHostName;
3892
+
3893
+
for (list.addrs) |addr| {
3894
+
const stream = std.net.tcpConnectToAddress(addr) catch |err| {
3895
+
log.warn("error connecting to host: {}", .{err});
3896
+
continue;
3897
+
};
3898
+
return stream;
3899
+
}
3900
+
return std.posix.ConnectError.ConnectionRefused;
3901
+
}
3902
3903
test "caseFold" {
3904
try testing.expect(caseFold("a", "A"));