1 #include <string.h>
2 #include "internal.h"
3
ncprogbar_create(ncplane * n,const ncprogbar_options * opts)4 ncprogbar* ncprogbar_create(ncplane* n, const ncprogbar_options* opts){
5 ncprogbar_options default_opts;
6 if(opts == NULL){
7 memset(&default_opts, 0, sizeof(default_opts));
8 opts = &default_opts;
9 }
10 if(opts->flags > (NCPROGBAR_OPTION_RETROGRADE << 1u)){
11 logwarn("Invalid flags %016" PRIx64 "\n", opts->flags);
12 }
13 ncprogbar* ret = malloc(sizeof(*ret));
14 if(ret == NULL){
15 ncplane_destroy(n);
16 return NULL;
17 }
18 ret->ncp = n;
19 ret->ulchannel = opts->ulchannel;
20 ret->urchannel = opts->urchannel;
21 ret->blchannel = opts->blchannel;
22 ret->brchannel = opts->brchannel;
23 ret->retrograde = opts->flags & NCPROGBAR_OPTION_RETROGRADE;
24 if(ncplane_set_widget(n, ret, (void(*)(void*))ncprogbar_destroy)){
25 ncplane_destroy(n);
26 free(ret);
27 return NULL;
28 }
29 return ret;
30 }
31
ncprogbar_plane(ncprogbar * n)32 ncplane* ncprogbar_plane(ncprogbar* n){
33 return n->ncp;
34 }
35
36 // we never explicitly draw full blocks, as any such cell is already
37 // handled (and at higher resolution) by the high-res gradient.
38 static const char right_egcs[8][5] = {
39 " ", "", "", "", "▐", "", "", "",
40 };
41
42 static const char left_egcs[8][5] = {
43 " ", "▏", "▎", "▍", "▌", "▋", "▊", "▉",
44 };
45
46 static const char down_egcs[8][5] = {
47 " ", "▔", "", "", "▀", "", "", "",
48 };
49
50 static const char up_egcs[8][5] = {
51 " ", "▁", "▂", "▃", "▄", "▅", "▆", "▇",
52 };
53
54 static int
progbar_redraw(ncprogbar * n)55 progbar_redraw(ncprogbar* n){
56 struct ncplane* ncp = ncprogbar_plane(n);
57 // get current dimensions; they might have changed
58 unsigned dimy, dimx;
59 ncplane_dim_yx(ncp, &dimy, &dimx);
60 const bool horizontal = dimx > dimy;
61 int range, delt, pos;
62 const char* egcs;
63 uint32_t ul, ur, bl, br;
64 if(horizontal){
65 range = dimx;
66 delt = 1;
67 pos = 0;
68 if(n->retrograde){
69 egcs = *right_egcs;
70 ul = n->urchannel; ur = n->brchannel;
71 bl = n->ulchannel; br = n->blchannel;
72 }else{
73 egcs = *left_egcs;
74 ul = n->blchannel; ur = n->ulchannel;
75 bl = n->brchannel; br = n->urchannel;
76 }
77 }else{
78 range = dimy;
79 delt = -1;
80 pos = range - 1;
81 if(n->retrograde){
82 egcs = *down_egcs;
83 ul = n->brchannel; ur = n->blchannel;
84 bl = n->urchannel; br = n->ulchannel;
85 }else{
86 egcs = *up_egcs;
87 ul = n->ulchannel; ur = n->urchannel;
88 bl = n->blchannel; br = n->brchannel;
89 }
90 }
91 ncplane_home(ncp);
92 if(notcurses_canutf8(ncplane_notcurses(ncp))){
93 if(ncplane_gradient2x1(ncp, -1, -1, 0, 0, ul, ur, bl, br) <= 0){
94 return -1;
95 }
96 }else{
97 if(ncplane_gradient(ncp, -1, -1, 0, 0, " ", 0, ul, ur, bl, br) <= 0){
98 return -1;
99 }
100 }
101 if(n->retrograde){
102 delt *= -1;
103 if(pos){
104 pos = 0;
105 }else{
106 pos = range - 1;
107 }
108 }
109 double eachcell = (1.0 / range); // how much each cell is worth
110 double chunk = n->progress;
111 const int chunks = n->progress / eachcell;
112 chunk -= eachcell * chunks;
113 pos += delt * chunks;
114 if(pos >= range){
115 return 0;
116 }
117 if(pos < 0){
118 return 0;
119 }
120 // at this point, we have a gradient across the entirety of the progress
121 // bar. we're going to first update the active frontier, which might need
122 // to cut down from a full block to a partial block. we'll then null out
123 // anything beyond the frontier.
124 const int egcidx = (int)(chunk / (eachcell / 8));
125 const char* egc = egcs + egcidx * 5;
126 if(horizontal){
127 for(unsigned freepos = 0 ; freepos < dimy ; ++freepos){
128 if(notcurses_canutf8(ncplane_notcurses(ncp))){
129 nccell* c = ncplane_cell_ref_yx(ncp, freepos, pos);
130 if(pool_blit_direct(&ncp->pool, c, egc, strlen(egc), 1) <= 0){
131 return -1;
132 }
133 cell_set_bchannel(c, 0);
134 }else{
135 if(ncplane_putchar_yx(ncp, freepos, pos, ' ') <= 0){
136 return -1;
137 }
138 }
139 }
140 }else{
141 for(unsigned freepos = 0 ; freepos < dimx ; ++freepos){
142 if(notcurses_canutf8(ncplane_notcurses(ncp))){
143 nccell* c = ncplane_cell_ref_yx(ncp, pos, freepos);
144 if(pool_blit_direct(&ncp->pool, c, egc, strlen(egc), 1) <= 0){
145 return -1;
146 }
147 cell_set_bchannel(c, 0);
148 }else{
149 if(ncplane_putchar_yx(ncp, pos, freepos, ' ') <= 0){
150 return -1;
151 }
152 }
153 }
154 }
155 // we now clear out all cells beyond the frontier.
156 pos += delt;
157 while(pos >= 0 && pos < range){
158 if(horizontal){
159 for(unsigned freepos = 0 ; freepos < dimy ; ++freepos){
160 nccell* c = ncplane_cell_ref_yx(ncp, freepos, pos);
161 nccell_release(ncp, c);
162 nccell_init(c);
163 }
164 }else{
165 for(unsigned freepos = 0 ; freepos < dimx ; ++freepos){
166 nccell* c = ncplane_cell_ref_yx(ncp, pos, freepos);
167 nccell_release(ncp, c);
168 nccell_init(c);
169 }
170 }
171 pos += delt;
172 }
173 return 0;
174 }
175
ncprogbar_set_progress(ncprogbar * n,double p)176 int ncprogbar_set_progress(ncprogbar* n, double p){
177 //fprintf(stderr, "PROGRESS: %g\n", p);
178 if(p < 0 || p > 1){
179 logerror("Invalid progress %g\n", p);
180 return -1;
181 }
182 n->progress = p;
183 return progbar_redraw(n);
184 }
185
ncprogbar_progress(const ncprogbar * n)186 double ncprogbar_progress(const ncprogbar* n){
187 return n->progress;
188 }
189
ncprogbar_destroy(ncprogbar * n)190 void ncprogbar_destroy(ncprogbar* n){
191 if(n){
192 if(ncplane_set_widget(n->ncp, NULL, NULL) == 0){
193 ncplane_destroy(n->ncp);
194 }
195 free(n);
196 }
197 }
198