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 }