1 #include "dropdown.h"
2 
3 #include "draw.h"
4 
5 #include "../macros.h"
6 #include "../settings.h"
7 #include "../theme.h"
8 
9 #include <stdlib.h>
10 #include <string.h>
11 
12 static DROPDOWN *active_dropdown;
13 static int       active_x, active_y, active_width, active_height;
14 
15 /* Show selected first, then skip selected */
16 #define index(d, i) (i == 0 ? d->selected : ((i > d->selected) ? i : i - 1))
17 
18 // Draw background rectangles for a dropdown
dropdown_drawactive(void)19 void dropdown_drawactive(void) {
20     DROPDOWN *drop = active_dropdown;
21     if (!drop) {
22         return;
23     }
24 
25     // load colors for this style
26     uint32_t color_bg, color_border, color_aoptbg, color_aopttext, color_text;
27 
28     switch (drop->style) {
29         case AUXILIARY_STYLE:
30             color_bg       = COLOR_BKGRND_AUX;
31             color_border   = COLOR_AUX_EDGE_ACTIVE;
32             color_aoptbg   = COLOR_AUX_ACTIVEOPTION_BKGRND;
33             color_aopttext = COLOR_AUX_ACTIVEOPTION_TEXT;
34             color_text     = COLOR_AUX_TEXT;
35             break;
36         default:
37             color_bg       = COLOR_BKGRND_MAIN;
38             color_border   = COLOR_EDGE_ACTIVE;
39             color_aoptbg   = COLOR_ACTIVEOPTION_BKGRND;
40             color_aopttext = COLOR_ACTIVEOPTION_TEXT;
41             color_text     = COLOR_MAIN_TEXT;
42             break;
43     }
44 
45     int x = active_x, y = active_y, w = active_width, h = active_height;
46 
47     int i, sign = 1;
48 
49     // Increase width if needed, so that all menu items fit.
50     for (i = 0; i != drop->dropcount; i++) {
51         STRING *e        = drop->ondisplay(i, drop);
52         int     needed_w = textwidth(e->str, e->length) + SCALE(8);
53         if (w < needed_w) {
54             w = needed_w;
55         }
56     }
57 
58     if (y + h * drop->dropcount > (int)settings.window_height) {
59         // y -= h * (drop->dropcount - 1);
60         // sign = -1;
61     }
62     y -= h * drop->selected;
63 
64     draw_rect_fill(x, y, w, h * drop->dropcount, color_bg);
65     draw_rect_frame(x, y, w, h * drop->dropcount, color_border);
66 
67     //if (sign == -1) {
68     //    y += h * (drop->dropcount - 1);
69     //}
70 
71     for (i = 0; i != drop->dropcount; i++) {
72         // int j = index(drop, i);
73         int     j = i;
74         STRING *e = drop->ondisplay(j, drop);
75         if (j == drop->over) {
76             draw_rect_fill(x + 1, y + 1, w - 2, h - 2, color_aoptbg);
77             setcolor(color_aopttext);
78         } else {
79             setcolor(color_text);
80         }
81         setfont(FONT_TEXT);
82         drawtext(x + SCALE(4), y + SCALE(4), e->str, e->length);
83 
84         y += sign * h;
85     }
86 }
87 
88 // Draw collapsed dropdown
dropdown_draw(DROPDOWN * d,int x,int y,int width,int height)89 void dropdown_draw(DROPDOWN *d, int x, int y, int width, int height) {
90     if (!d->open) {
91         // load colors for this style
92         uint32_t color_bg, color_border, color_border_h, color_text;
93 
94         switch (d->style) {
95             case AUXILIARY_STYLE:
96                 color_bg       = COLOR_BKGRND_AUX;
97                 color_border   = COLOR_AUX_EDGE_NORMAL;
98                 color_border_h = COLOR_AUX_EDGE_HOVER;
99                 color_text     = COLOR_AUX_TEXT;
100                 break;
101             default:
102                 color_bg       = COLOR_BKGRND_MAIN;
103                 color_border   = COLOR_EDGE_NORMAL;
104                 color_border_h = COLOR_EDGE_HOVER;
105                 color_text     = COLOR_MAIN_TEXT;
106                 break;
107         }
108 
109         draw_rect_frame(x, y, width, height, (d->mouseover ? color_border_h : color_border));
110         draw_rect_fill(x + 1, y + 1, width - 2, height - 2, color_bg);
111 
112         if (d->dropcount) {
113             setfont(FONT_TEXT);
114             setcolor(color_text);
115             STRING *text = d->ondisplay(d->selected, d);
116             drawtextwidth(x + SCALE(4), width - SCALE(8), y + SCALE(4), text->str, text->length);
117         }
118     } else {
119         active_x      = x;
120         active_y      = y;
121         active_width  = width;
122         active_height = height;
123     }
124 }
125 
dropdown_mmove(DROPDOWN * d,int UNUSED (x),int y,int w,int h,int mx,int my,int UNUSED (dx),int UNUSED (dy))126 bool dropdown_mmove(DROPDOWN *d, int UNUSED(x), int y, int w, int h, int mx, int my, int UNUSED(dx), int UNUSED(dy)) {
127     if (d->open) {
128         bool mouseover;
129 
130         if (my > 0) {
131             mouseover = inrect(mx, my, 0, 0, w, MIN(h * d->dropcount, (int)settings.window_height));
132         } else {
133             mouseover = mx >= 0 && mx <= w && abs(my) <= h * d->selected;
134         }
135 
136         if (d->mouseover != mouseover) {
137             d->mouseover = mouseover;
138         }
139 
140         if (mouseover) {
141             d->skip_mup = true;
142         } else {
143             d->over     = false;
144             d->skip_mup = false;
145             return true;
146         }
147 
148         int over = my / h + d->selected;
149 
150         if (y + h * d->dropcount > (int)settings.window_height) {
151             // over = my > 0 ? 0 : ((-my) / h + 1);
152         }
153 
154         if (my < 0)
155             over--;
156 
157         if (over < d->dropcount) {
158             // over = index(d, over);
159             if (over != d->over) {
160                 d->over = over;
161                 return true;
162             }
163         }
164     } else {
165         bool mouseover = inrect(mx, my, 0, 0, w, h);
166         if (d->mouseover != mouseover) {
167             d->mouseover = mouseover;
168             return true;
169         }
170     }
171 
172     return false;
173 }
174 
dropdown_mdown(DROPDOWN * d)175 bool dropdown_mdown(DROPDOWN *d) {
176     if (d->mouseover && d->dropcount) {
177         d->open         = true;
178         active_dropdown = d;
179         return true;
180     }
181 
182     if (d->skip_mup) {
183         return dropdown_close(d);
184     }
185 
186     return false;
187 }
188 
dropdown_close(DROPDOWN * d)189 bool dropdown_close(DROPDOWN *d) {
190     d->open = false;
191     active_dropdown = NULL;
192     return true;
193 }
194 
dropdown_mright(DROPDOWN * UNUSED (d))195 bool dropdown_mright(DROPDOWN *UNUSED(d)) {
196     return false;
197 }
198 
dropdown_mwheel(DROPDOWN * UNUSED (d),int UNUSED (height),double UNUSED (dlta),bool UNUSED (smooth))199 bool dropdown_mwheel(DROPDOWN *UNUSED(d), int UNUSED(height), double UNUSED(dlta), bool UNUSED(smooth)) {
200     return false;
201 }
202 
dropdown_mup(DROPDOWN * d)203 bool dropdown_mup(DROPDOWN *d) {
204     if (d->open) {
205         if (!d->mouseover) {
206             return dropdown_close(d);
207         }
208 
209         if (d->skip_mup) {
210             d->skip_mup = false;
211             dropdown_close(d);
212 
213             if (d->over < d->dropcount) {
214                 d->selected = d->over;
215                 d->onselect(d->selected, d);
216             }
217 
218             return true;
219         } else {
220             d->skip_mup = true;
221         }
222 
223         return false;
224     }
225 
226     return false;
227 }
228 
dropdown_mleave(DROPDOWN * d)229 bool dropdown_mleave(DROPDOWN *d) {
230     if (d->mouseover) {
231         d->mouseover = false;
232         return true;
233     }
234 
235     return false;
236 }
237 
238 /***** list-based dropdown menu start *****/
239 
240 // Appends localization-independent menu item.
dropdown_list_add_hardcoded(DROPDOWN * d,char * name,void * handle)241 void dropdown_list_add_hardcoded(DROPDOWN *d, char *name, void *handle) {
242     void *p = realloc(d->userdata, (d->dropcount + 1) * sizeof(DROP_ELEMENT));
243     if (!p) {
244         return;
245     }
246     d->userdata = p;
247 
248     DROP_ELEMENT *e = &((DROP_ELEMENT *)d->userdata)[d->dropcount++];
249     maybe_i18nal_string_set_plain(&e->name, name, strlen((char *)name));
250     e->handle = handle;
251 }
252 
253 // Appends localized menu item.
dropdown_list_add_localized(DROPDOWN * d,UTOX_I18N_STR string_id,void * handle)254 void dropdown_list_add_localized(DROPDOWN *d, UTOX_I18N_STR string_id, void *handle) {
255     void *p = realloc(d->userdata, (d->dropcount + 1) * sizeof(DROP_ELEMENT));
256     if (!p) {
257         return;
258     }
259     d->userdata = p;
260 
261     DROP_ELEMENT *e = &((DROP_ELEMENT *)d->userdata)[d->dropcount++];
262     maybe_i18nal_string_set_i18nal(&e->name, string_id);
263     e->handle = handle;
264 }
265 
266 // Clears menu (removes all menu items of a list-based dropdown).
dropdown_list_clear(DROPDOWN * d)267 void dropdown_list_clear(DROPDOWN *d) {
268     free(d->userdata);
269     d->userdata  = NULL;
270     d->dropcount = 0;
271     d->over      = false;
272     d->selected  = 0;
273 }
274 
275 // Generic display function for list-based dropdowns,
276 // userdata of which is an array of DROP_ELEMENTs.
dropdown_list_ondisplay(uint16_t i,const DROPDOWN * dm)277 STRING *dropdown_list_ondisplay(uint16_t i, const DROPDOWN *dm) {
278     DROP_ELEMENT *e = &((DROP_ELEMENT *)dm->userdata)[i];
279     return maybe_i18nal_string_get(&e->name);
280 }
281 
282 /***** list-based dropdown menu end *****/
283 
284 /***** simple localized dropdown menu start *****/
285 
286 // Generic display function for simple dropdowns,
287 // userdata of which is a simple array of UI_STRING_IDs.
simple_dropdown_ondisplay(uint16_t i,const DROPDOWN * dm)288 STRING *simple_dropdown_ondisplay(uint16_t i, const DROPDOWN *dm) {
289     return SPTRFORLANG(settings.language, ((UTOX_I18N_STR *)dm->userdata)[i]);
290 }
291 
292 /***** simple localized dropdown menu end *****/
293