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