1 /**
2 * Consoleur: a package for interaction with character-oriented terminal emulators
3 *
4 * Copyright: Maxim Freck, 2017.
5 * Authors: Maxim Freck <maxim@freck.pp.ru>
6 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7 */
8 module consoleur.cli.progress;
9
10 class Progress
11 {
12 import std.stdio: stderr, stdout, writef, writeln;
13 import consoleur.core, consoleur.cursor, consoleur.screen, consoleur.cli.util;
14 public:
15
16 /*******
17 * Constructor
18 *
19 * Params:
20 * title = Progress bar title
21 */
22 this(string title = "")
23 {
24 stdout.flush();
25 savePosition();
26 setTitle(title);
27 }
28
29 /*******
30 * Sets new progress bar title
31 *
32 * Params:
33 * title = Progress bar title
34 */
35 void setTitle(string title)
36 {
37 this.title = title;
38 }
39
40 /*******
41 * Sets new progress bar percents
42 *
43 * Params:
44 * pct = Percent
45 */
46 void setPercent(int pct)
47 {
48 if (isAttyOut()) {
49 setPercentTty(pct);
50 } else {
51 setPercentRedirected(pct);
52 }
53 }
54
55 private:
56 enum FILLED_PERCENT = 0;
57 enum EMPTY_PERCENT = 1;
58 enum OPEN_BAR = 2;
59 enum CLOSE_BAR = 3;
60
61 string title;
62 ubyte filledColor = 0;
63 int row;
64
65 wchar[4] blocks = ['█', '░', '┨', '┠'];
66
67 wchar[] spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
68
69 ///Spinner examples
70 //wchar[] spinner = ['▖','▘','▝','▗'];
71 //wchar[] spinner = ['▌','▀','▐','▄'];
72 //wchar[] spinner = ['▉','▊','▋','▌','▍','▎','▏','▎','▍','▌','▋','▊','▉'];
73 //wchar[] spinner = ['▁','▃','▄','▅','▆','▇','█','▇','▆','▅','▄','▃'];
74 //wchar[] spinner = ['☱','☲','☴'];
75 //wchar[] spinner = ['⠋','⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏'];
76 //wchar[] spinner = ['⠋','⠙','⠚','⠞','⠖','⠦','⠴','⠲','⠳','⠓'];
77 //wchar[] spinner = ['⠄','⠆','⠇','⠋','⠙','⠸','⠰','⠠','⠰','⠸','⠙','⠋','⠇','⠆'];
78 //wchar[] spinner = ['⠋','⠙','⠚','⠒','⠂','⠂','⠒','⠲','⠴','⠦','⠖','⠒','⠐','⠐','⠒','⠓','⠋'];
79 //wchar[] spinner = ['⠁','⠉','⠙','⠚','⠒','⠂','⠂','⠒','⠲','⠴','⠤','⠄','⠄','⠤','⠴','⠲','⠒','⠂','⠂','⠒','⠚','⠙','⠉','⠁'];
80 //wchar[] spinner = ['⠈','⠉','⠋','⠓','⠒','⠐','⠐','⠒','⠖','⠦','⠤','⠠','⠠','⠤','⠦','⠖','⠒','⠐','⠐','⠒','⠓','⠋','⠉','⠈'];
81 //wchar[] spinner = ['⠁','⠁','⠉','⠙','⠚','⠒','⠂','⠂','⠒','⠲','⠴','⠤','⠄','⠄','⠤','⠠','⠠','⠤','⠦','⠖','⠒','⠐','⠐','⠒','⠓','⠋','⠉','⠈','⠈'];
82 //wchar[] spinner = ['⢄','⢂','⢁','⡁','⡈','⡐','⡠'];
83 //wchar[] spinner = ['⢹','⢺','⢼','⣸','⣇','⡧','⡗','⡏'];
84 //wchar[] spinner = ['⣾','⣽','⣻','⢿','⡿','⣟','⣯','⣷'];
85 //wchar[] spinner = ['⠁','⠂','⠄','⡀','⢀','⠠','⠐','⠈'];
86
87 void setPercentTty(int pct)
88 {
89 if (pct < 0) pct = 0;
90 if (pct > 100) pct = 100;
91
92
93 if (row > 0) {
94 moveCursorTo(Point(row, 1));
95 } else {
96 saveCursorPosition();
97 }
98
99 uint width = cast(uint)(getScreenSize().col - 11 - title.length);
100 uint done = cast(uint)((width*pct)/100);
101
102 writef(" %s %c %c%s%s%c%3s%% ",
103 title,
104 progressSymbol(pct),
105 blocks[OPEN_BAR],
106 repeat(blocks[FILLED_PERCENT], done),
107 repeat(blocks[EMPTY_PERCENT], width - done),
108 blocks[CLOSE_BAR],
109 pct,
110 );
111 stdout.flush();
112
113 if (row <= 0) restoreCursorPosition();
114 }
115
116 void setPercentRedirected(int pct)
117 {
118 import std.conv: to;
119 writeln(title~to!string(pct));
120 }
121
122 void savePosition()
123 {
124 if (!isAttyOut()) return;
125
126 import std.stdio: writeln;
127
128 immutable pos = getCursorPosition();
129 row = pos.row;
130 if (pos.col > 1) {
131 auto window = getScreenSize();
132 if(window.row == row) {
133 writeln();
134 } else {
135 row+=1;
136 }
137 }
138 }
139
140 wchar progressSymbol(int percent)
141 {
142 return spinner[percent % spinner.length];
143 }
144 }