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