-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprogressbar.odin
More file actions
128 lines (114 loc) · 4.03 KB
/
progressbar.odin
File metadata and controls
128 lines (114 loc) · 4.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package progressbar
import "core:fmt"
import "core:time"
import "base:intrinsics"
import "shared:afmt"
// Specific Progress Bar types
String :: struct { data: string, format: afmt.ANSI }
Rune :: struct { data: rune, format: afmt.ANSI }
Glyph :: union { Rune, [][]Rune }
// Progress Bar struct
Progress_Bar :: struct {
width: uint,
title: String,
lbracket: String,
rbracket: String,
percent: String,
done: Rune,
undone: Rune,
glyph: Glyph,
state: uint
}
// Helper proc to make [][]Rune Glyph variant
make_glyph_multi :: proc(states, runes: int, allocator := context.allocator) -> (glyph: [][]Rune) {
glyph = make([][]Rune, states, allocator)
for s in 0..<states {
glyph[s] = make([]Rune, runes, allocator)
}
return
}
// Delete Progress_Bar if it is of the multi type
// Will ignore progress bars that do not allocate, so safe to use on all of them
delete_progress_bar :: proc(bar_format: ^Progress_Bar) {
#partial switch glyph in bar_format.glyph {
case [][]Rune:
for g in glyph { delete(g) }
delete(glyph)
}
}
// Cursor show/hide/return
cursor_hide :: proc(flush := true) { fmt.print("\e[?25l", flush=flush) }
cursor_show :: proc(flush := true) { fmt.print("\e[?25h", flush=flush) }
cursor_return :: proc(flush := true) { fmt.print("\r", flush=flush) }
// Use after a loop ends in case progress did not finish
progress_bar_exit :: proc() { fmt.println(flush = true); cursor_show(flush = true) }
// Draw Progress_Bar
progress_bar :: proc(pb: ^Progress_Bar, percent: $T) where intrinsics.type_is_numeric(T) {
when ODIN_OS == .Windows {
afmt.set_utf8_terminal()
defer afmt.reset_utf8_terminal()
}
upercent := int(abs(percent))
width := int(pb.width)
done := upercent * width / 100
undone := width - done
// Extract glyph slice from Progress_Bar
glyph: []Rune
switch g in pb.glyph {
case Rune:
glyph = { g }
case [][]Rune:
if done != 0 && (done - int(pb.state)) % 2 == 0 {
pb.state = pb.state < len(g) - 1 ? pb.state + 1 : 0
}
glyph = g[pb.state][:]
}
// Make room for glyph
if upercent <= 100 {
if len(glyph) >= done { glyph = glyph[len(glyph)-done:] }
done -= len(glyph)
} else { // for animating finish
right_index := len(glyph) - upercent + 100
// Check if glyph is larger than bar width
// Some bars may stylistically choose to have longer glyphs than bar width
if len(glyph) > width {
left_index := len(glyph) - upercent + 100 - width
left_index = left_index < 0 ? 0 : left_index
glyph = glyph[left_index:right_index]
} else {
glyph = glyph[:right_index]
}
undone = 0
done = width - len(glyph)
}
// Print progress bar
cursor_return(flush = false)
cursor_hide(flush = false)
afmt.printf("%v", pb.title.format, pb.title.data, flush=false)
afmt.printf("%v", pb.lbracket.format, pb.lbracket.data, flush=false)
for _ in 0..<done { afmt.printf("%v", pb.done.format, pb.done.data, flush=false) }
for g in glyph { afmt.printf("%r", g.format, g.data, flush=false) }
for _ in 0..<undone { afmt.printf("%v", pb.undone.format, pb.undone.data, flush=false) }
afmt.printf("%s", pb.rbracket.format, pb.rbracket.data, flush=false)
if intrinsics.type_is_float(T) {
afmt.printf("%6.2f%s", pb.percent.format, percent > 100 ? 100 : percent, pb.percent.data, flush=false)
} else {
afmt.printf("%3i%s", pb.percent.format, percent > 100 ? 100 : percent, pb.percent.data, flush=false)
}
// Finish - animating glyph outro as needed
if upercent >= 100 {
if len(glyph) != 0 {
// speed = 1 second / 2^n starting at 2^3 where n increments every 1/4 ratio of len(glyph):width
// then mulitpled by every factor of width over 50 to increase speed for long bars
out_speed: time.Duration
switch f32(len(glyph)) / f32(width) {
case 0.000..<0.250: out_speed = 8 * time.Duration(width) / 50
case 0.250..<0.500: out_speed = 16 * time.Duration(width) / 50
case 0.500..<0.750: out_speed = 32 * time.Duration(width) / 50
case /* 0.750+ */ : out_speed = 64 * time.Duration(width) / 50
}
time.sleep(time.Second / out_speed)
progress_bar(pb, percent + 1)
}
}
}