Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / interface / interface_widgets.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version. 
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2009 Blender Foundation.
19  * All rights reserved.
20  * 
21  * Contributor(s): Blender Foundation
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/editors/interface/interface_widgets.c
27  *  \ingroup edinterface
28  */
29
30 #include <limits.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <assert.h>
34
35 #include "DNA_brush_types.h"
36 #include "DNA_screen_types.h"
37 #include "DNA_userdef_types.h"
38
39 #include "BLI_math.h"
40 #include "BLI_rect.h"
41 #include "BLI_string.h"
42 #include "BLI_string_utf8.h"
43 #include "BLI_utildefines.h"
44
45 #include "BKE_context.h"
46 #include "BKE_curve.h"
47
48 #include "RNA_access.h"
49
50 #include "BIF_gl.h"
51 #include "BIF_glutil.h"
52
53 #include "BLF_api.h"
54
55 #include "UI_interface.h"
56 #include "UI_interface_icons.h"
57
58 #include "interface_intern.h"
59
60 #include "GPU_basic_shader.h"
61 #include "GPU_immediate.h"
62 #include "GPU_matrix.h"
63
64 #ifdef WITH_INPUT_IME
65 #  include "WM_types.h"
66 #endif
67
68 /* icons are 80% of height of button (16 pixels inside 20 height) */
69 #define ICON_SIZE_FROM_BUTRECT(rect) (0.8f * BLI_rcti_size_y(rect))
70
71 /* ************** widget base functions ************** */
72 /**
73  * - in: roundbox codes for corner types and radius
74  * - return: array of `[size][2][x, y]` points, the edges of the roundbox, + UV coords
75  *
76  * - draw black box with alpha 0 on exact button boundbox
77  * - for every AA step:
78  *    - draw the inner part for a round filled box, with color blend codes or texture coords
79  *    - draw outline in outline color
80  *    - draw outer part, bottom half, extruded 1 pixel to bottom, for emboss shadow
81  *    - draw extra decorations
82  * - draw background color box with alpha 1 on exact button boundbox
83  */
84
85 /* fill this struct with polygon info to draw AA'ed */
86 /* it has outline, back, and two optional tria meshes */
87
88 typedef struct uiWidgetTrias {
89         unsigned int tot;
90         
91         float vec[16][2];
92         const unsigned int (*index)[3];
93         
94 } uiWidgetTrias;
95
96 /* max as used by round_box__edges */
97 #define WIDGET_CURVE_RESOLU 9
98 #define WIDGET_SIZE_MAX (WIDGET_CURVE_RESOLU * 4)
99
100 typedef struct uiWidgetBase {
101         
102         int totvert, halfwayvert;
103         float outer_v[WIDGET_SIZE_MAX][2];
104         float inner_v[WIDGET_SIZE_MAX][2];
105         float inner_uv[WIDGET_SIZE_MAX][2];
106         
107         bool draw_inner, draw_outline, draw_emboss, draw_shadedir;
108         
109         uiWidgetTrias tria1;
110         uiWidgetTrias tria2;
111         
112 } uiWidgetBase;
113
114 /** uiWidgetType: for time being only for visual appearance,
115  * later, a handling callback can be added too 
116  */
117 typedef struct uiWidgetType {
118         
119         /* pointer to theme color definition */
120         uiWidgetColors *wcol_theme;
121         uiWidgetStateColors *wcol_state;
122         
123         /* converted colors for state */
124         uiWidgetColors wcol;
125         
126         void (*state)(struct uiWidgetType *, int state);
127         void (*draw)(uiWidgetColors *, rcti *, int state, int roundboxalign);
128         void (*custom)(uiBut *, uiWidgetColors *, rcti *, int state, int roundboxalign);
129         void (*text)(uiFontStyle *, uiWidgetColors *, uiBut *, rcti *);
130         
131 } uiWidgetType;
132
133
134 /* *********************** draw data ************************** */
135
136 static const float cornervec[WIDGET_CURVE_RESOLU][2] = {
137         {0.0, 0.0}, {0.195, 0.02}, {0.383, 0.067},
138         {0.55, 0.169}, {0.707, 0.293}, {0.831, 0.45},
139         {0.924, 0.617}, {0.98, 0.805}, {1.0, 1.0}
140 };
141
142 #define WIDGET_AA_JITTER 8
143 static const float jit[WIDGET_AA_JITTER][2] = {
144         { 0.468813, -0.481430}, {-0.155755, -0.352820},
145         { 0.219306, -0.238501}, {-0.393286, -0.110949},
146         {-0.024699,  0.013908}, { 0.343805,  0.147431},
147         {-0.272855,  0.269918}, { 0.095909,  0.388710}
148 };
149
150 static const float num_tria_vert[3][2] = {
151         {-0.352077, 0.532607}, {-0.352077, -0.549313}, {0.330000, -0.008353}
152 };
153
154 static const unsigned int num_tria_face[1][3] = {
155         {0, 1, 2}
156 };
157
158 static const float scroll_circle_vert[16][2] = {
159         {0.382684, 0.923879}, {0.000001, 1.000000}, {-0.382683, 0.923880}, {-0.707107, 0.707107},
160         {-0.923879, 0.382684}, {-1.000000, 0.000000}, {-0.923880, -0.382684}, {-0.707107, -0.707107},
161         {-0.382683, -0.923880}, {0.000000, -1.000000}, {0.382684, -0.923880}, {0.707107, -0.707107},
162         {0.923880, -0.382684}, {1.000000, -0.000000}, {0.923880, 0.382683}, {0.707107, 0.707107}
163 };
164
165 static const unsigned int scroll_circle_face[14][3] = {
166         {0, 1, 2}, {2, 0, 3}, {3, 0, 15}, {3, 15, 4}, {4, 15, 14}, {4, 14, 5}, {5, 14, 13}, {5, 13, 6},
167         {6, 13, 12}, {6, 12, 7}, {7, 12, 11}, {7, 11, 8}, {8, 11, 10}, {8, 10, 9}
168 };
169
170
171 static const float menu_tria_vert[6][2] = {
172         {-0.33, 0.16}, {0.33, 0.16}, {0, 0.82},
173         {0, -0.82}, {-0.33, -0.16}, {0.33, -0.16}
174 };
175
176
177
178 static const unsigned int menu_tria_face[2][3] = {{2, 0, 1}, {3, 5, 4}};
179
180 static const float check_tria_vert[6][2] = {
181         {-0.578579, 0.253369},  {-0.392773, 0.412794},  {-0.004241, -0.328551},
182         {-0.003001, 0.034320},  {1.055313, 0.864744},   {0.866408, 1.026895}
183 };
184
185 static const unsigned int check_tria_face[4][3] = {
186         {3, 2, 4}, {3, 4, 5}, {1, 0, 3}, {0, 2, 3}
187 };
188
189 /* ************************************************* */
190
191 void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3,
192                        const float color[4])
193 {
194         float tri_arr[3][2] = {{x1, y1}, {x2, y2}, {x3, y3}};
195         float draw_color[4];
196
197         copy_v4_v4(draw_color, color);
198         draw_color[3] *= 0.125f;
199
200         glEnable(GL_BLEND);
201
202         unsigned int pos = add_attrib(immVertexFormat(), "pos", GL_FLOAT, 2, KEEP_FLOAT);
203         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
204
205         immUniformColor4fv(draw_color);
206         immBegin(GL_TRIANGLES, 3 * WIDGET_AA_JITTER);
207
208         /* for each AA step */
209         for (int j = 0; j < WIDGET_AA_JITTER; j++) {
210                 immVertex2f(pos, tri_arr[0][0] + jit[j][0], tri_arr[0][1] + jit[j][1]);
211                 immVertex2f(pos, tri_arr[1][0] + jit[j][0], tri_arr[1][1] + jit[j][1]);
212                 immVertex2f(pos, tri_arr[2][0] + jit[j][0], tri_arr[2][1] + jit[j][1]);
213         }
214
215         immEnd();
216
217         immUnbindProgram();
218
219         glDisable(GL_BLEND);
220 }
221
222 void ui_draw_anti_roundbox(int mode, float minx, float miny, float maxx, float maxy,
223                            float rad, bool use_alpha, const float color[4])
224 {
225         float draw_color[4];
226         int j;
227
228         copy_v4_v4(draw_color, color);
229
230         glEnable(GL_BLEND);
231         if (use_alpha) {
232                 draw_color[3] = 0.5f;
233         }
234         draw_color[3] *= 0.125f;
235         
236         for (j = 0; j < WIDGET_AA_JITTER; j++) {
237                 glTranslate2fv(jit[j]);
238                 UI_draw_roundbox_gl_mode(mode, minx, miny, maxx, maxy, rad, draw_color);
239                 glTranslatef(-jit[j][0], -jit[j][1], 0.0f);
240         }
241
242         glDisable(GL_BLEND);
243 }
244
245 static void widget_init(uiWidgetBase *wtb)
246 {
247         wtb->totvert = wtb->halfwayvert = 0;
248         wtb->tria1.tot = 0;
249         wtb->tria2.tot = 0;
250
251         wtb->draw_inner = true;
252         wtb->draw_outline = true;
253         wtb->draw_emboss = true;
254         wtb->draw_shadedir = true;
255 }
256
257 /* helper call, makes shadow rect, with 'sun' above menu, so only shadow to left/right/bottom */
258 /* return tot */
259 static int round_box_shadow_edges(float (*vert)[2], const rcti *rect, float rad, int roundboxalign, float step)
260 {
261         float vec[WIDGET_CURVE_RESOLU][2];
262         float minx, miny, maxx, maxy;
263         int a, tot = 0;
264         
265         rad += step;
266         
267         if (2.0f * rad > BLI_rcti_size_y(rect))
268                 rad = 0.5f * BLI_rcti_size_y(rect);
269
270         minx = rect->xmin - step;
271         miny = rect->ymin - step;
272         maxx = rect->xmax + step;
273         maxy = rect->ymax + step;
274         
275         /* mult */
276         for (a = 0; a < WIDGET_CURVE_RESOLU; a++) {
277                 vec[a][0] = rad * cornervec[a][0];
278                 vec[a][1] = rad * cornervec[a][1];
279         }
280         
281         /* start with left-top, anti clockwise */
282         if (roundboxalign & UI_CNR_TOP_LEFT) {
283                 for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
284                         vert[tot][0] = minx + rad - vec[a][0];
285                         vert[tot][1] = maxy - vec[a][1];
286                 }
287         }
288         else {
289                 for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
290                         vert[tot][0] = minx;
291                         vert[tot][1] = maxy;
292                 }
293         }
294         
295         if (roundboxalign & UI_CNR_BOTTOM_LEFT) {
296                 for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
297                         vert[tot][0] = minx + vec[a][1];
298                         vert[tot][1] = miny + rad - vec[a][0];
299                 }
300         }
301         else {
302                 for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
303                         vert[tot][0] = minx;
304                         vert[tot][1] = miny;
305                 }
306         }
307         
308         if (roundboxalign & UI_CNR_BOTTOM_RIGHT) {
309                 for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
310                         vert[tot][0] = maxx - rad + vec[a][0];
311                         vert[tot][1] = miny + vec[a][1];
312                 }
313         }
314         else {
315                 for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
316                         vert[tot][0] = maxx;
317                         vert[tot][1] = miny;
318                 }
319         }
320         
321         if (roundboxalign & UI_CNR_TOP_RIGHT) {
322                 for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
323                         vert[tot][0] = maxx - vec[a][1];
324                         vert[tot][1] = maxy - rad + vec[a][0];
325                 }
326         }
327         else {
328                 for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
329                         vert[tot][0] = maxx;
330                         vert[tot][1] = maxy;
331                 }
332         }
333         return tot;
334 }
335
336 /* this call has 1 extra arg to allow mask outline */
337 static void round_box__edges(uiWidgetBase *wt, int roundboxalign, const rcti *rect, float rad, float radi)
338 {
339         float vec[WIDGET_CURVE_RESOLU][2], veci[WIDGET_CURVE_RESOLU][2];
340         float minx = rect->xmin, miny = rect->ymin, maxx = rect->xmax, maxy = rect->ymax;
341         float minxi = minx + U.pixelsize; /* boundbox inner */
342         float maxxi = maxx - U.pixelsize;
343         float minyi = miny + U.pixelsize;
344         float maxyi = maxy - U.pixelsize;
345         float facxi = (maxxi != minxi) ? 1.0f / (maxxi - minxi) : 0.0f; /* for uv, can divide by zero */
346         float facyi = (maxyi != minyi) ? 1.0f / (maxyi - minyi) : 0.0f;
347         int a, tot = 0, minsize;
348         const int hnum = ((roundboxalign & (UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT)) == (UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT) ||
349                           (roundboxalign & (UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT)) == (UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT)) ? 1 : 2;
350         const int vnum = ((roundboxalign & (UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT)) == (UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT) ||
351                           (roundboxalign & (UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT)) == (UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT)) ? 1 : 2;
352
353         minsize = min_ii(BLI_rcti_size_x(rect) * hnum,
354                          BLI_rcti_size_y(rect) * vnum);
355         
356         if (2.0f * rad > minsize)
357                 rad = 0.5f * minsize;
358
359         if (2.0f * (radi + 1.0f) > minsize)
360                 radi = 0.5f * minsize - U.pixelsize;
361         
362         /* mult */
363         for (a = 0; a < WIDGET_CURVE_RESOLU; a++) {
364                 veci[a][0] = radi * cornervec[a][0];
365                 veci[a][1] = radi * cornervec[a][1];
366                 vec[a][0] = rad * cornervec[a][0];
367                 vec[a][1] = rad * cornervec[a][1];
368         }
369         
370         /* corner left-bottom */
371         if (roundboxalign & UI_CNR_BOTTOM_LEFT) {
372                 
373                 for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
374                         wt->inner_v[tot][0] = minxi + veci[a][1];
375                         wt->inner_v[tot][1] = minyi + radi - veci[a][0];
376                         
377                         wt->outer_v[tot][0] = minx + vec[a][1];
378                         wt->outer_v[tot][1] = miny + rad - vec[a][0];
379                         
380                         wt->inner_uv[tot][0] = facxi * (wt->inner_v[tot][0] - minxi);
381                         wt->inner_uv[tot][1] = facyi * (wt->inner_v[tot][1] - minyi);
382                 }
383         }
384         else {
385                 wt->inner_v[tot][0] = minxi;
386                 wt->inner_v[tot][1] = minyi;
387                 
388                 wt->outer_v[tot][0] = minx;
389                 wt->outer_v[tot][1] = miny;
390
391                 wt->inner_uv[tot][0] = 0.0f;
392                 wt->inner_uv[tot][1] = 0.0f;
393                 
394                 tot++;
395         }
396         
397         /* corner right-bottom */
398         if (roundboxalign & UI_CNR_BOTTOM_RIGHT) {
399                 
400                 for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
401                         wt->inner_v[tot][0] = maxxi - radi + veci[a][0];
402                         wt->inner_v[tot][1] = minyi + veci[a][1];
403                         
404                         wt->outer_v[tot][0] = maxx - rad + vec[a][0];
405                         wt->outer_v[tot][1] = miny + vec[a][1];
406                         
407                         wt->inner_uv[tot][0] = facxi * (wt->inner_v[tot][0] - minxi);
408                         wt->inner_uv[tot][1] = facyi * (wt->inner_v[tot][1] - minyi);
409                 }
410         }
411         else {
412                 wt->inner_v[tot][0] = maxxi;
413                 wt->inner_v[tot][1] = minyi;
414                 
415                 wt->outer_v[tot][0] = maxx;
416                 wt->outer_v[tot][1] = miny;
417
418                 wt->inner_uv[tot][0] = 1.0f;
419                 wt->inner_uv[tot][1] = 0.0f;
420                 
421                 tot++;
422         }
423         
424         wt->halfwayvert = tot;
425         
426         /* corner right-top */
427         if (roundboxalign & UI_CNR_TOP_RIGHT) {
428                 
429                 for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
430                         wt->inner_v[tot][0] = maxxi - veci[a][1];
431                         wt->inner_v[tot][1] = maxyi - radi + veci[a][0];
432                         
433                         wt->outer_v[tot][0] = maxx - vec[a][1];
434                         wt->outer_v[tot][1] = maxy - rad + vec[a][0];
435                         
436                         wt->inner_uv[tot][0] = facxi * (wt->inner_v[tot][0] - minxi);
437                         wt->inner_uv[tot][1] = facyi * (wt->inner_v[tot][1] - minyi);
438                 }
439         }
440         else {
441                 wt->inner_v[tot][0] = maxxi;
442                 wt->inner_v[tot][1] = maxyi;
443                 
444                 wt->outer_v[tot][0] = maxx;
445                 wt->outer_v[tot][1] = maxy;
446                 
447                 wt->inner_uv[tot][0] = 1.0f;
448                 wt->inner_uv[tot][1] = 1.0f;
449                 
450                 tot++;
451         }
452         
453         /* corner left-top */
454         if (roundboxalign & UI_CNR_TOP_LEFT) {
455                 
456                 for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
457                         wt->inner_v[tot][0] = minxi + radi - veci[a][0];
458                         wt->inner_v[tot][1] = maxyi - veci[a][1];
459                         
460                         wt->outer_v[tot][0] = minx + rad - vec[a][0];
461                         wt->outer_v[tot][1] = maxy - vec[a][1];
462                         
463                         wt->inner_uv[tot][0] = facxi * (wt->inner_v[tot][0] - minxi);
464                         wt->inner_uv[tot][1] = facyi * (wt->inner_v[tot][1] - minyi);
465                 }
466                 
467         }
468         else {
469                 
470                 wt->inner_v[tot][0] = minxi;
471                 wt->inner_v[tot][1] = maxyi;
472                 
473                 wt->outer_v[tot][0] = minx;
474                 wt->outer_v[tot][1] = maxy;
475                 
476                 wt->inner_uv[tot][0] = 0.0f;
477                 wt->inner_uv[tot][1] = 1.0f;
478                 
479                 tot++;
480         }
481
482         BLI_assert(tot <= WIDGET_SIZE_MAX);
483
484         wt->totvert = tot;
485 }
486
487 static void round_box_edges(uiWidgetBase *wt, int roundboxalign, const rcti *rect, float rad)
488 {
489         round_box__edges(wt, roundboxalign, rect, rad, rad - U.pixelsize);
490 }
491
492
493 /* based on button rect, return scaled array of triangles */
494 static void widget_draw_tria_ex(
495         uiWidgetTrias *tria, const rcti *rect, float triasize, char where,
496         /* input data */
497         const float verts[][2], const int verts_tot,
498         const unsigned int tris[][3], const int tris_tot)
499 {
500         float centx, centy, sizex, sizey, minsize;
501         int a, i1 = 0, i2 = 1;
502
503         minsize = min_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect));
504
505         /* center position and size */
506         centx = (float)rect->xmin + 0.4f * minsize;
507         centy = (float)rect->ymin + 0.5f * minsize;
508         sizex = sizey = -0.5f * triasize * minsize;
509
510         if (where == 'r') {
511                 centx = (float)rect->xmax - 0.4f * minsize;
512                 sizex = -sizex;
513         }
514         else if (where == 't') {
515                 centy = (float)rect->ymax - 0.5f * minsize;
516                 sizey = -sizey;
517                 i2 = 0; i1 = 1;
518         }
519         else if (where == 'b') {
520                 sizex = -sizex;
521                 i2 = 0; i1 = 1;
522         }
523
524         for (a = 0; a < verts_tot; a++) {
525                 tria->vec[a][0] = sizex * verts[a][i1] + centx;
526                 tria->vec[a][1] = sizey * verts[a][i2] + centy;
527         }
528
529         tria->tot = tris_tot;
530         tria->index = tris;
531 }
532
533 static void widget_num_tria(uiWidgetTrias *tria, const rcti *rect, float triasize, char where)
534 {
535         widget_draw_tria_ex(
536                 tria, rect, triasize, where,
537                 num_tria_vert, ARRAY_SIZE(num_tria_vert),
538                 num_tria_face, ARRAY_SIZE(num_tria_face));
539 }
540
541 static void widget_scroll_circle(uiWidgetTrias *tria, const rcti *rect, float triasize, char where)
542 {
543         widget_draw_tria_ex(
544                 tria, rect, triasize, where,
545                 scroll_circle_vert, ARRAY_SIZE(scroll_circle_vert),
546                 scroll_circle_face, ARRAY_SIZE(scroll_circle_face));
547 }
548
549 static void widget_trias_draw(uiWidgetTrias *tria, unsigned int pos)
550 {
551         immBegin(GL_TRIANGLES, tria->tot * 3);
552         for (int i = 0; i < tria->tot; ++i)
553                 for (int j = 0; j < 3; ++j)
554                         immVertex2fv(pos, tria->vec[tria->index[i][j]]);
555         immEnd();
556 }
557
558 static void widget_draw_vertex_buffer(unsigned int pos, unsigned int col, int mode,
559                                       const float quads_pos[WIDGET_SIZE_MAX][2],
560                                       const unsigned char quads_col[WIDGET_SIZE_MAX][4],
561                                       unsigned int totvert)
562 {
563         immBegin(mode, totvert);
564         for (int i = 0; i < totvert; ++i) {
565                 if (quads_col)
566                         immAttrib4ubv(col, quads_col[i]);
567                 immVertex2fv(pos, quads_pos[i]);
568         }
569         immEnd();
570 }
571
572 static void widget_menu_trias(uiWidgetTrias *tria, const rcti *rect)
573 {
574         float centx, centy, size;
575         int a;
576
577         /* center position and size */
578         centx = rect->xmax - 0.32f * BLI_rcti_size_y(rect);
579         centy = rect->ymin + 0.50f * BLI_rcti_size_y(rect);
580         size = 0.4f * BLI_rcti_size_y(rect);
581
582         for (a = 0; a < 6; a++) {
583                 tria->vec[a][0] = size * menu_tria_vert[a][0] + centx;
584                 tria->vec[a][1] = size * menu_tria_vert[a][1] + centy;
585         }
586
587         tria->tot = 2;
588         tria->index = menu_tria_face;
589 }
590
591 static void widget_check_trias(uiWidgetTrias *tria, const rcti *rect)
592 {
593         float centx, centy, size;
594         int a;
595         
596         /* center position and size */
597         centx = rect->xmin + 0.5f * BLI_rcti_size_y(rect);
598         centy = rect->ymin + 0.5f * BLI_rcti_size_y(rect);
599         size = 0.5f * BLI_rcti_size_y(rect);
600         
601         for (a = 0; a < 6; a++) {
602                 tria->vec[a][0] = size * check_tria_vert[a][0] + centx;
603                 tria->vec[a][1] = size * check_tria_vert[a][1] + centy;
604         }
605         
606         tria->tot = 4;
607         tria->index = check_tria_face;
608 }
609
610
611 /* prepares shade colors */
612 static void shadecolors4(char coltop[4], char coldown[4], const char *color, short shadetop, short shadedown)
613 {
614         coltop[0] = CLAMPIS(color[0] + shadetop, 0, 255);
615         coltop[1] = CLAMPIS(color[1] + shadetop, 0, 255);
616         coltop[2] = CLAMPIS(color[2] + shadetop, 0, 255);
617         coltop[3] = color[3];
618
619         coldown[0] = CLAMPIS(color[0] + shadedown, 0, 255);
620         coldown[1] = CLAMPIS(color[1] + shadedown, 0, 255);
621         coldown[2] = CLAMPIS(color[2] + shadedown, 0, 255);
622         coldown[3] = color[3];
623 }
624
625 static void round_box_shade_col4_r(unsigned char r_col[4], const char col1[4], const char col2[4], const float fac)
626 {
627         const int faci = FTOCHAR(fac);
628         const int facm = 255 - faci;
629
630         r_col[0] = (faci * col1[0] + facm * col2[0]) / 256;
631         r_col[1] = (faci * col1[1] + facm * col2[1]) / 256;
632         r_col[2] = (faci * col1[2] + facm * col2[2]) / 256;
633         r_col[3] = (faci * col1[3] + facm * col2[3]) / 256;
634 }
635
636 static void widget_verts_to_triangle_strip(uiWidgetBase *wtb, const int totvert, float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2])
637 {
638         int a;
639         for (a = 0; a < totvert; a++) {
640                 copy_v2_v2(triangle_strip[a * 2], wtb->outer_v[a]);
641                 copy_v2_v2(triangle_strip[a * 2 + 1], wtb->inner_v[a]);
642         }
643         copy_v2_v2(triangle_strip[a * 2], wtb->outer_v[0]);
644         copy_v2_v2(triangle_strip[a * 2 + 1], wtb->inner_v[0]);
645 }
646
647 static void widget_verts_to_triangle_strip_open(uiWidgetBase *wtb, const int totvert, float triangle_strip[WIDGET_SIZE_MAX * 2][2])
648 {
649         int a;
650         for (a = 0; a < totvert; a++) {
651                 triangle_strip[a * 2][0] = wtb->outer_v[a][0];
652                 triangle_strip[a * 2][1] = wtb->outer_v[a][1];
653                 triangle_strip[a * 2 + 1][0] = wtb->outer_v[a][0];
654                 triangle_strip[a * 2 + 1][1] = wtb->outer_v[a][1] - 1.0f;
655         }
656 }
657
658 static void widgetbase_outline(uiWidgetBase *wtb, unsigned int pos)
659 {
660         float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2]; /* + 2 because the last pair is wrapped */
661         widget_verts_to_triangle_strip(wtb, wtb->totvert, triangle_strip);
662
663         widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_STRIP, triangle_strip, NULL, wtb->totvert * 2 + 2);
664 }
665
666 static void widgetbase_draw(uiWidgetBase *wtb, uiWidgetColors *wcol)
667 {
668         int j, a;
669
670         glEnable(GL_BLEND);
671
672         /* backdrop non AA */
673         if (wtb->draw_inner) {
674                 if (wcol->shaded == 0) {
675                         if (wcol->alpha_check) {
676                                 float inner_v_half[WIDGET_SIZE_MAX][2];
677                                 float x_mid = 0.0f; /* used for dumb clamping of values */
678
679                                 unsigned int pos = add_attrib(immVertexFormat(), "pos", GL_FLOAT, 2, KEEP_FLOAT);
680                                 immBindBuiltinProgram(GPU_SHADER_2D_CHECKER);
681
682                                 /* checkers */
683                                 immUniform4f("color1", UI_ALPHA_CHECKER_DARK / 255.0f, UI_ALPHA_CHECKER_DARK / 255.0f, UI_ALPHA_CHECKER_DARK / 255.0f, 1.0f);
684                                 immUniform4f("color2", UI_ALPHA_CHECKER_LIGHT / 255.0f, UI_ALPHA_CHECKER_LIGHT / 255.0f, UI_ALPHA_CHECKER_LIGHT / 255.0f, 1.0f);
685                                 immUniform1i("size", 8);
686
687                                 widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_FAN, wtb->inner_v, NULL, wtb->totvert);
688
689                                 immUnbindProgram();
690
691                                 /* alpha fill */
692                                 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
693                                 immUniformColor4ubv((unsigned char *)wcol->inner);
694
695                                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
696
697                                 for (a = 0; a < wtb->totvert; a++) {
698                                         x_mid += wtb->inner_v[a][0];
699                                 }
700                                 x_mid /= wtb->totvert;
701
702                                 widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_FAN, wtb->inner_v, NULL, wtb->totvert);
703
704                                 /* 1/2 solid color */
705                                 immUniformColor4ub(wcol->inner[0], wcol->inner[1], wcol->inner[2], 255);
706
707                                 for (a = 0; a < wtb->totvert; a++) {
708                                         inner_v_half[a][0] = MIN2(wtb->inner_v[a][0], x_mid);
709                                         inner_v_half[a][1] = wtb->inner_v[a][1];
710                                 }
711
712                                 widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_FAN, inner_v_half, NULL, wtb->totvert);
713
714                                 immUnbindProgram();
715                         }
716                         else {
717                                 /* simple fill */
718                                 unsigned int pos = add_attrib(immVertexFormat(), "pos", GL_FLOAT, 2, KEEP_FLOAT);
719                                 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
720                                 immUniformColor4ubv((unsigned char *)wcol->inner);
721
722                                 widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_FAN, wtb->inner_v, NULL, wtb->totvert);
723
724                                 immUnbindProgram();
725                         }
726                 }
727                 else {
728                         char col1[4], col2[4];
729                         unsigned char col_array[WIDGET_SIZE_MAX][4];
730                         unsigned char *col_pt = &col_array[0][0];
731
732                         VertexFormat *format = immVertexFormat();
733                         unsigned int pos = add_attrib(format, "pos", GL_FLOAT, 2, KEEP_FLOAT);
734                         unsigned int col = add_attrib(format, "color", GL_UNSIGNED_BYTE, 4, NORMALIZE_INT_TO_FLOAT);
735
736                         immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
737
738                         shadecolors4(col1, col2, wcol->inner, wcol->shadetop, wcol->shadedown);
739
740                         for (a = 0; a < wtb->totvert; a++, col_pt += 4) {
741                                 round_box_shade_col4_r(col_pt, col1, col2, wtb->inner_uv[a][wtb->draw_shadedir ? 1 : 0]);
742                         }
743
744                         widget_draw_vertex_buffer(pos, col, GL_TRIANGLE_FAN, wtb->inner_v, col_array, wtb->totvert);
745                         immUnbindProgram();
746                 }
747         }
748
749         /* for each AA step */
750         if (wtb->draw_outline) {
751                 float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2]; /* + 2 because the last pair is wrapped */
752                 float triangle_strip_emboss[WIDGET_SIZE_MAX * 2][2]; /* only for emboss */
753
754                 const unsigned char tcol[4] = {wcol->outline[0],
755                                                wcol->outline[1],
756                                                wcol->outline[2],
757                                                wcol->outline[3] / WIDGET_AA_JITTER};
758                 unsigned char emboss[4];
759
760                 widget_verts_to_triangle_strip(wtb, wtb->totvert, triangle_strip);
761
762                 if (wtb->draw_emboss) {
763                         widget_verts_to_triangle_strip_open(wtb, wtb->halfwayvert, triangle_strip_emboss);
764                         UI_GetThemeColor4ubv(TH_WIDGET_EMBOSS, emboss);
765                 }
766
767                 unsigned int pos = add_attrib(immVertexFormat(), "pos", GL_FLOAT, 2, KEEP_FLOAT);
768
769                 gpuMatrixBegin3D_legacy();
770
771                 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
772                 for (j = 0; j < WIDGET_AA_JITTER; j++) {
773                         gpuTranslate3f(jit[j][0], jit[j][1], 0.0f);
774                         
775                         /* outline */
776                         immUniformColor4ubv(tcol);
777
778                         widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_STRIP, triangle_strip, NULL, wtb->totvert * 2 + 2);
779
780                         /* emboss bottom shadow */
781                         if (wtb->draw_emboss) {
782                                 if (emboss[3]) {
783                                         immUniformColor4ubv(emboss);
784                                         widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_STRIP, triangle_strip_emboss, NULL, wtb->halfwayvert * 2);
785                                 }
786                         }
787                         
788                         gpuTranslate3f(-jit[j][0], -jit[j][1], 0.0f);
789                 }
790                 immUnbindProgram();
791
792                 gpuMatrixEnd();
793         }
794
795         /* decoration */
796         if (wtb->tria1.tot || wtb->tria2.tot) {
797                 const unsigned char tcol[4] = {wcol->item[0],
798                                                wcol->item[1],
799                                                wcol->item[2],
800                                                (unsigned char)((float)wcol->item[3] / WIDGET_AA_JITTER)};
801
802                 unsigned int pos = add_attrib(immVertexFormat(), "pos", GL_FLOAT, 2, KEEP_FLOAT);
803                 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
804                 immUniformColor4ubv(tcol);
805
806                 gpuMatrixBegin3D_legacy();
807
808                 /* for each AA step */
809                 for (j = 0; j < WIDGET_AA_JITTER; j++) {
810                         gpuTranslate3f(jit[j][0], jit[j][1], 0.0f);
811
812                         if (wtb->tria1.tot)
813                                 widget_trias_draw(&wtb->tria1, pos);
814
815                         if (wtb->tria2.tot)
816                                 widget_trias_draw(&wtb->tria2, pos);
817                 
818                         gpuTranslate3f(-jit[j][0], -jit[j][1], 0.0f);
819                 }
820
821                 gpuMatrixEnd();
822
823                 immUnbindProgram();
824         }
825
826         glDisable(GL_BLEND);
827 }
828
829 /* *********************** text/icon ************************************** */
830
831 #define UI_TEXT_CLIP_MARGIN (0.25f * U.widget_unit / but->block->aspect)
832
833 #define PREVIEW_PAD 4
834
835 static void widget_draw_preview(BIFIconID icon, float alpha, const rcti *rect)
836 {
837         int w, h, size;
838
839         if (icon == ICON_NONE)
840                 return;
841
842         w = BLI_rcti_size_x(rect);
843         h = BLI_rcti_size_y(rect);
844         size = MIN2(w, h);
845         size -= PREVIEW_PAD * 2;  /* padding */
846
847         if (size > 0) {
848                 int x = rect->xmin + w / 2 - size / 2;
849                 int y = rect->ymin + h / 2 - size / 2;
850
851                 UI_icon_draw_preview_aspect_size(x, y, icon, 1.0f, alpha, size);
852         }
853 }
854
855
856 static int ui_but_draw_menu_icon(const uiBut *but)
857 {
858         return (but->flag & UI_BUT_ICON_SUBMENU) && (but->dt == UI_EMBOSS_PULLDOWN);
859 }
860
861 /* icons have been standardized... and this call draws in untransformed coordinates */
862
863 static void widget_draw_icon(
864         const uiBut *but, BIFIconID icon, float alpha, const rcti *rect,
865         const bool show_menu_icon)
866 {
867         float xs = 0.0f, ys = 0.0f;
868         float aspect, height;
869         
870         if (but->flag & UI_BUT_ICON_PREVIEW) {
871                 glEnable(GL_BLEND);
872                 widget_draw_preview(icon, alpha, rect);
873                 glDisable(GL_BLEND);
874                 return;
875         }
876         
877         /* this icon doesn't need draw... */
878         if (icon == ICON_BLANK1 && (but->flag & UI_BUT_ICON_SUBMENU) == 0) return;
879         
880         aspect = but->block->aspect / UI_DPI_FAC;
881         height = ICON_DEFAULT_HEIGHT / aspect;
882
883         /* calculate blend color */
884         if (ELEM(but->type, UI_BTYPE_TOGGLE, UI_BTYPE_ROW, UI_BTYPE_TOGGLE_N, UI_BTYPE_LISTROW)) {
885                 if (but->flag & UI_SELECT) {}
886                 else if (but->flag & UI_ACTIVE) {}
887                 else alpha = 0.5f;
888         }
889         else if ((but->type == UI_BTYPE_LABEL)) {
890                 /* extra feature allows more alpha blending */
891                 if (but->a1 == 1.0f) {
892                         alpha *= but->a2;
893                 }
894         }
895         else if (ELEM(but->type, UI_BTYPE_BUT)) {
896                 if (but->flag & UI_BUT_DISABLED) {
897                         alpha *= 0.5f;
898                 }
899         }
900         
901         glEnable(GL_BLEND);
902         
903         if (icon && icon != ICON_BLANK1) {
904                 float ofs = 1.0f / aspect;
905                 
906                 if (but->drawflag & UI_BUT_ICON_LEFT) {
907                         if (but->block->flag & UI_BLOCK_LOOP) {
908                                 if (but->type == UI_BTYPE_SEARCH_MENU)
909                                         xs = rect->xmin + 4.0f * ofs;
910                                 else
911                                         xs = rect->xmin + ofs;
912                         }
913                         else {
914                                 if (but->dt == UI_EMBOSS_NONE || but->type == UI_BTYPE_LABEL)
915                                         xs = rect->xmin + 2.0f * ofs;
916                                 else
917                                         xs = rect->xmin + 4.0f * ofs;
918                         }
919                         ys = (rect->ymin + rect->ymax - height) / 2.0f;
920                 }
921                 else {
922                         xs = (rect->xmin + rect->xmax - height) / 2.0f;
923                         ys = (rect->ymin + rect->ymax - height) / 2.0f;
924                 }
925
926                 /* force positions to integers, for zoom levels near 1. draws icons crisp. */
927                 if (aspect > 0.95f && aspect < 1.05f) {
928                         xs = (int)(xs + 0.1f);
929                         ys = (int)(ys + 0.1f);
930                 }
931                 
932                 /* to indicate draggable */
933                 if (but->dragpoin && (but->flag & UI_ACTIVE)) {
934                         float rgb[3] = {1.25f, 1.25f, 1.25f};
935                         UI_icon_draw_aspect_color(xs, ys, icon, aspect, rgb);
936                 }
937                 else
938                         UI_icon_draw_aspect(xs, ys, icon, aspect, alpha);
939         }
940
941         if (show_menu_icon) {
942                 xs = rect->xmax - UI_DPI_ICON_SIZE - aspect;
943                 ys = (rect->ymin + rect->ymax - height) / 2.0f;
944                 
945                 UI_icon_draw_aspect(xs, ys, ICON_RIGHTARROW_THIN, aspect, alpha);
946         }
947         
948         glDisable(GL_BLEND);
949 }
950
951 static void ui_text_clip_give_prev_off(uiBut *but, const char *str)
952 {
953         const char *prev_utf8 = BLI_str_find_prev_char_utf8(str, str + but->ofs);
954         int bytes = str + but->ofs - prev_utf8;
955
956         but->ofs -= bytes;
957 }
958
959 static void ui_text_clip_give_next_off(uiBut *but, const char *str)
960 {
961         const char *next_utf8 = BLI_str_find_next_char_utf8(str + but->ofs, NULL);
962         int bytes = next_utf8 - (str + but->ofs);
963
964         but->ofs += bytes;
965 }
966
967 /**
968  * Helper.
969  * This func assumes things like kerning handling have already been handled!
970  * Return the length of modified (right-clipped + ellipsis) string.
971  */
972 static void ui_text_clip_right_ex(
973         uiFontStyle *fstyle, char *str, const size_t max_len, const float okwidth,
974         const char *sep, const int sep_len, const float sep_strwidth, size_t *r_final_len)
975 {
976         float tmp;
977         int l_end;
978
979         BLI_assert(str[0]);
980
981         /* If the trailing ellipsis takes more than 20% of all available width, just cut the string
982          * (as using the ellipsis would remove even more useful chars, and we cannot show much already!).
983          */
984         if (sep_strwidth / okwidth > 0.2f) {
985                 l_end = BLF_width_to_strlen(fstyle->uifont_id, str, max_len, okwidth, &tmp);
986                 str[l_end] = '\0';
987                 if (r_final_len) {
988                         *r_final_len = (size_t)l_end;
989                 }
990         }
991         else {
992                 l_end = BLF_width_to_strlen(fstyle->uifont_id, str, max_len, okwidth - sep_strwidth, &tmp);
993                 memcpy(str + l_end, sep, sep_len + 1);  /* +1 for trailing '\0'. */
994                 if (r_final_len) {
995                         *r_final_len = (size_t)(l_end + sep_len);
996                 }
997         }
998 }
999
1000 /**
1001  * Cut off the middle of the text to fit into the given width.
1002  * Note in case this middle clipping would just remove a few chars, it rather clips right, which is more readable.
1003  * If rpart_sep is not Null, the part of str starting to first occurrence of rpart_sep is preserved at all cost (useful
1004  * for strings with shortcuts, like 'AVeryLongFooBarLabelForMenuEntry|Ctrl O' -> 'AVeryLong...MenuEntry|Ctrl O').
1005  */
1006 float UI_text_clip_middle_ex(
1007         uiFontStyle *fstyle, char *str, float okwidth, const float minwidth,
1008         const size_t max_len, const char rpart_sep)
1009 {
1010         float strwidth;
1011
1012         /* Add some epsilon to OK width, avoids 'ellipsing' text that nearly fits!
1013          * Better to have a small piece of the last char cut out, than two remaining chars replaced by an ellipsis... */
1014         okwidth += 1.0f + UI_DPI_FAC;
1015
1016         BLI_assert(str[0]);
1017
1018         /* need to set this first */
1019         UI_fontstyle_set(fstyle);
1020
1021         if (fstyle->kerning == 1) {  /* for BLF_width */
1022                 BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
1023         }
1024
1025         strwidth = BLF_width(fstyle->uifont_id, str, max_len);
1026
1027         if ((okwidth > 0.0f) && (strwidth > okwidth)) {
1028                 /* utf8 ellipsis '...', some compilers complain */
1029                 const char sep[] = {0xe2, 0x80, 0xa6, 0x0};
1030                 const int sep_len = sizeof(sep) - 1;
1031                 const float sep_strwidth = BLF_width(fstyle->uifont_id, sep, sep_len + 1);
1032                 float parts_strwidth;
1033                 size_t l_end;
1034
1035                 char *rpart = NULL, rpart_buf[UI_MAX_DRAW_STR];
1036                 float rpart_width = 0.0f;
1037                 size_t rpart_len = 0;
1038                 size_t final_lpart_len;
1039
1040                 if (rpart_sep) {
1041                         rpart = strrchr(str, rpart_sep);
1042
1043                         if (rpart) {
1044                                 rpart_len = strlen(rpart);
1045                                 rpart_width = BLF_width(fstyle->uifont_id, rpart, rpart_len);
1046                                 okwidth -= rpart_width;
1047                                 strwidth -= rpart_width;
1048
1049                                 if (okwidth < 0.0f) {
1050                                         /* Not enough place for actual label, just display protected right part.
1051                                          * Here just for safety, should never happen in real life! */
1052                                         memmove(str, rpart, rpart_len + 1);
1053                                         rpart = NULL;
1054                                         okwidth += rpart_width;
1055                                         strwidth = rpart_width;
1056                                 }
1057                         }
1058                 }
1059
1060                 parts_strwidth = (okwidth - sep_strwidth) / 2.0f;
1061
1062                 if (rpart) {
1063                         strcpy(rpart_buf, rpart);
1064                         *rpart = '\0';
1065                         rpart = rpart_buf;
1066                 }
1067
1068                 l_end = BLF_width_to_strlen(fstyle->uifont_id, str, max_len, parts_strwidth, &rpart_width);
1069                 if (l_end < 10 || min_ff(parts_strwidth, strwidth - okwidth) < minwidth) {
1070                         /* If we really have no place, or we would clip a very small piece of string in the middle,
1071                          * only show start of string.
1072                          */
1073                         ui_text_clip_right_ex(fstyle, str, max_len, okwidth, sep, sep_len, sep_strwidth, &final_lpart_len);
1074                 }
1075                 else {
1076                         size_t r_offset, r_len;
1077
1078                         r_offset = BLF_width_to_rstrlen(fstyle->uifont_id, str, max_len, parts_strwidth, &rpart_width);
1079                         r_len = strlen(str + r_offset) + 1;  /* +1 for the trailing '\0'. */
1080
1081                         if (l_end + sep_len + r_len + rpart_len > max_len) {
1082                                 /* Corner case, the str already takes all available mem, and the ellipsis chars would actually
1083                                  * add more chars...
1084                                  * Better to just trim one or two letters to the right in this case...
1085                                  * Note: with a single-char ellipsis, this should never happen! But better be safe here...
1086                                  */
1087                                 ui_text_clip_right_ex(fstyle, str, max_len, okwidth, sep, sep_len, sep_strwidth, &final_lpart_len);
1088                         }
1089                         else {
1090                                 memmove(str + l_end + sep_len, str + r_offset, r_len);
1091                                 memcpy(str + l_end, sep, sep_len);
1092                                 final_lpart_len = (size_t)(l_end + sep_len + r_len - 1);  /* -1 to remove trailing '\0'! */
1093                         }
1094                 }
1095
1096                 if (rpart) {
1097                         /* Add back preserved right part to our shorten str. */
1098                         memcpy(str + final_lpart_len, rpart, rpart_len + 1);  /* +1 for trailing '\0'. */
1099                 }
1100
1101                 strwidth = BLF_width(fstyle->uifont_id, str, max_len);
1102         }
1103
1104         if (fstyle->kerning == 1) {
1105                 BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
1106         }
1107
1108         return strwidth;
1109 }
1110
1111 /**
1112  * Wrapper around UI_text_clip_middle_ex.
1113  */
1114 static void ui_text_clip_middle(uiFontStyle *fstyle, uiBut *but, const rcti *rect)
1115 {
1116         /* No margin for labels! */
1117         const int border = ELEM(but->type, UI_BTYPE_LABEL, UI_BTYPE_MENU) ? 0 : (int)(UI_TEXT_CLIP_MARGIN + 0.5f);
1118         const float okwidth = (float)max_ii(BLI_rcti_size_x(rect) - border, 0);
1119         const size_t max_len = sizeof(but->drawstr);
1120         const float minwidth = (float)(UI_DPI_ICON_SIZE) / but->block->aspect * 2.0f;
1121
1122         but->ofs = 0;
1123         but->strwidth = UI_text_clip_middle_ex(fstyle, but->drawstr, okwidth, minwidth, max_len, '\0');
1124 }
1125
1126 /**
1127  * Like ui_text_clip_middle(), but protect/preserve at all cost the right part of the string after sep.
1128  * Useful for strings with shortcuts (like 'AVeryLongFooBarLabelForMenuEntry|Ctrl O' -> 'AVeryLong...MenuEntry|Ctrl O').
1129  */
1130 static void ui_text_clip_middle_protect_right(uiFontStyle *fstyle, uiBut *but, const rcti *rect, const char rsep)
1131 {
1132         /* No margin for labels! */
1133         const int border = ELEM(but->type, UI_BTYPE_LABEL, UI_BTYPE_MENU) ? 0 : (int)(UI_TEXT_CLIP_MARGIN + 0.5f);
1134         const float okwidth = (float)max_ii(BLI_rcti_size_x(rect) - border, 0);
1135         const size_t max_len = sizeof(but->drawstr);
1136         const float minwidth = (float)(UI_DPI_ICON_SIZE) / but->block->aspect * 2.0f;
1137
1138         but->ofs = 0;
1139         but->strwidth = UI_text_clip_middle_ex(fstyle, but->drawstr, okwidth, minwidth, max_len, rsep);
1140 }
1141
1142 /**
1143  * Cut off the text, taking into account the cursor location (text display while editing).
1144  */
1145 static void ui_text_clip_cursor(uiFontStyle *fstyle, uiBut *but, const rcti *rect)
1146 {
1147         const int border = (int)(UI_TEXT_CLIP_MARGIN + 0.5f);
1148         const int okwidth = max_ii(BLI_rcti_size_x(rect) - border, 0);
1149
1150         BLI_assert(but->editstr && but->pos >= 0);
1151
1152         /* need to set this first */
1153         UI_fontstyle_set(fstyle);
1154
1155         if (fstyle->kerning == 1) /* for BLF_width */
1156                 BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
1157
1158         /* define ofs dynamically */
1159         if (but->ofs > but->pos)
1160                 but->ofs = but->pos;
1161
1162         if (BLF_width(fstyle->uifont_id, but->editstr, INT_MAX) <= okwidth)
1163                 but->ofs = 0;
1164
1165         but->strwidth = BLF_width(fstyle->uifont_id, but->editstr + but->ofs, INT_MAX);
1166
1167         if (but->strwidth > okwidth) {
1168                 int len = strlen(but->editstr);
1169
1170                 while (but->strwidth > okwidth) {
1171                         float width;
1172
1173                         /* string position of cursor */
1174                         width = BLF_width(fstyle->uifont_id, but->editstr + but->ofs, (but->pos - but->ofs));
1175
1176                         /* if cursor is at 20 pixels of right side button we clip left */
1177                         if (width > okwidth - 20) {
1178                                 ui_text_clip_give_next_off(but, but->editstr);
1179                         }
1180                         else {
1181                                 int bytes;
1182                                 /* shift string to the left */
1183                                 if (width < 20 && but->ofs > 0)
1184                                         ui_text_clip_give_prev_off(but, but->editstr);
1185                                 bytes = BLI_str_utf8_size(BLI_str_find_prev_char_utf8(but->editstr, but->editstr + len));
1186                                 if (bytes == -1)
1187                                         bytes = 1;
1188                                 len -= bytes;
1189                         }
1190
1191                         but->strwidth = BLF_width(fstyle->uifont_id, but->editstr + but->ofs, len - but->ofs);
1192
1193                         if (but->strwidth < 10) break;
1194                 }
1195         }
1196
1197         if (fstyle->kerning == 1) {
1198                 BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
1199         }
1200 }
1201
1202 /**
1203  * Cut off the end of text to fit into the width of \a rect.
1204  *
1205  * \note deals with ': ' especially for number buttons
1206  */
1207 static void ui_text_clip_right_label(uiFontStyle *fstyle, uiBut *but, const rcti *rect)
1208 {
1209         const int border = UI_TEXT_CLIP_MARGIN + 1;
1210         const int okwidth = max_ii(BLI_rcti_size_x(rect) - border, 0);
1211         char *cpoin = NULL;
1212         int drawstr_len = strlen(but->drawstr);
1213         const char *cpend = but->drawstr + drawstr_len;
1214         
1215         /* need to set this first */
1216         UI_fontstyle_set(fstyle);
1217         
1218         if (fstyle->kerning == 1) /* for BLF_width */
1219                 BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
1220         
1221         but->strwidth = BLF_width(fstyle->uifont_id, but->drawstr, sizeof(but->drawstr));
1222         but->ofs = 0;
1223         
1224
1225         /* First shorten num-buttons eg,
1226          *   Translucency: 0.000
1227          * becomes
1228          *   Trans: 0.000
1229          */
1230
1231         /* find the space after ':' separator */
1232         cpoin = strrchr(but->drawstr, ':');
1233         
1234         if (cpoin && (cpoin < cpend - 2)) {
1235                 char *cp2 = cpoin;
1236                 
1237                 /* chop off the leading text, starting from the right */
1238                 while (but->strwidth > okwidth && cp2 > but->drawstr) {
1239                         const char *prev_utf8 = BLI_str_find_prev_char_utf8(but->drawstr, cp2);
1240                         int bytes = cp2 - prev_utf8;
1241
1242                         /* shift the text after and including cp2 back by 1 char, +1 to include null terminator */
1243                         memmove(cp2 - bytes, cp2, drawstr_len + 1);
1244                         cp2 -= bytes;
1245
1246                         drawstr_len -= bytes;
1247                         // BLI_assert(strlen(but->drawstr) == drawstr_len);
1248                         
1249                         but->strwidth = BLF_width(fstyle->uifont_id, but->drawstr + but->ofs, sizeof(but->drawstr) - but->ofs);
1250                         if (but->strwidth < 10) break;
1251                 }
1252         
1253         
1254                 /* after the leading text is gone, chop off the : and following space, with ofs */
1255                 while ((but->strwidth > okwidth) && (but->ofs < 2)) {
1256                         ui_text_clip_give_next_off(but, but->drawstr);
1257                         but->strwidth = BLF_width(fstyle->uifont_id, but->drawstr + but->ofs, sizeof(but->drawstr) - but->ofs);
1258                         if (but->strwidth < 10) break;
1259                 }
1260         }
1261
1262
1263         /* Now just remove trailing chars */
1264         /* once the label's gone, chop off the least significant digits */
1265         if (but->strwidth > okwidth) {
1266                 float strwidth;
1267                 drawstr_len = BLF_width_to_strlen(fstyle->uifont_id, but->drawstr + but->ofs,
1268                                                   drawstr_len - but->ofs, okwidth, &strwidth) + but->ofs;
1269                 but->strwidth = strwidth;
1270                 but->drawstr[drawstr_len] = 0;
1271         }
1272         
1273         if (fstyle->kerning == 1)
1274                 BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
1275 }
1276
1277 #ifdef WITH_INPUT_IME
1278 static void widget_draw_text_ime_underline(
1279         uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *but, const rcti *rect,
1280         const wmIMEData *ime_data, const char *drawstr)
1281 {
1282         int ofs_x, width;
1283         int rect_x = BLI_rcti_size_x(rect);
1284         int sel_start = ime_data->sel_start, sel_end = ime_data->sel_end;
1285         float fcol[4];
1286
1287         if (drawstr[0] != 0) {
1288                 if (but->pos >= but->ofs) {
1289                         ofs_x = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->pos - but->ofs);
1290                 }
1291                 else {
1292                         ofs_x = 0;
1293                 }
1294
1295                 width = BLF_width(fstyle->uifont_id, drawstr + but->ofs,
1296                                   ime_data->composite_len + but->pos - but->ofs);
1297
1298                 rgba_uchar_to_float(fcol, wcol->text);
1299                 UI_draw_text_underline(rect->xmin + ofs_x, rect->ymin + 6 * U.pixelsize, min_ii(width, rect_x - 2) - ofs_x, 1, fcol);
1300
1301                 /* draw the thick line */
1302                 if (sel_start != -1 && sel_end != -1) {
1303                         sel_end -= sel_start;
1304                         sel_start += but->pos;
1305
1306                         if (sel_start >= but->ofs) {
1307                                 ofs_x = BLF_width(fstyle->uifont_id, drawstr + but->ofs, sel_start - but->ofs);
1308                         }
1309                         else {
1310                                 ofs_x = 0;
1311                         }
1312
1313                         width = BLF_width(fstyle->uifont_id, drawstr + but->ofs,
1314                                           sel_end + sel_start - but->ofs);
1315
1316                         UI_draw_text_underline(rect->xmin + ofs_x, rect->ymin + 6 * U.pixelsize, min_ii(width, rect_x - 2) - ofs_x, 2, fcol);
1317                 }
1318         }
1319 }
1320 #endif  /* WITH_INPUT_IME */
1321
1322 static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *but, rcti *rect)
1323 {
1324         int drawstr_left_len = UI_MAX_DRAW_STR;
1325         const char *drawstr = but->drawstr;
1326         const char *drawstr_right = NULL;
1327         bool use_right_only = false;
1328
1329 #ifdef WITH_INPUT_IME
1330         const wmIMEData *ime_data;
1331 #endif
1332
1333         UI_fontstyle_set(fstyle);
1334         
1335         if (but->editstr || (but->drawflag & UI_BUT_TEXT_LEFT))
1336                 fstyle->align = UI_STYLE_TEXT_LEFT;
1337         else if (but->drawflag & UI_BUT_TEXT_RIGHT)
1338                 fstyle->align = UI_STYLE_TEXT_RIGHT;
1339         else
1340                 fstyle->align = UI_STYLE_TEXT_CENTER;
1341         
1342         if (fstyle->kerning == 1) /* for BLF_width */
1343                 BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
1344         
1345
1346         /* Special case: when we're entering text for multiple buttons,
1347          * don't draw the text for any of the multi-editing buttons */
1348         if (UNLIKELY(but->flag & UI_BUT_DRAG_MULTI)) {
1349                 uiBut *but_edit = ui_but_drag_multi_edit_get(but);
1350                 if (but_edit) {
1351                         drawstr = but_edit->editstr;
1352                         fstyle->align = UI_STYLE_TEXT_LEFT;
1353                 }
1354         }
1355         else {
1356                 if (but->editstr) {
1357                         /* max length isn't used in this case,
1358                          * we rely on string being NULL terminated. */
1359                         drawstr_left_len = INT_MAX;
1360
1361 #ifdef WITH_INPUT_IME
1362                         /* FIXME, IME is modifying 'const char *drawstr! */
1363                         ime_data = ui_but_ime_data_get(but);
1364
1365                         if (ime_data && ime_data->composite_len) {
1366                                 /* insert composite string into cursor pos */
1367                                 BLI_snprintf((char *)drawstr, UI_MAX_DRAW_STR, "%s%s%s",
1368                                              but->editstr, ime_data->str_composite,
1369                                              but->editstr + but->pos);
1370                         }
1371                         else
1372 #endif
1373                         {
1374                                 drawstr = but->editstr;
1375                         }
1376                 }
1377         }
1378
1379
1380         /* text button selection, cursor, composite underline */
1381         if (but->editstr && but->pos != -1) {
1382                 int but_pos_ofs;
1383                 int tx, ty;
1384
1385                 /* text button selection */
1386                 if ((but->selend - but->selsta) > 0) {
1387                         int selsta_draw, selwidth_draw;
1388                         
1389                         if (drawstr[0] != 0) {
1390
1391                                 if (but->selsta >= but->ofs) {
1392                                         selsta_draw = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->selsta - but->ofs);
1393                                 }
1394                                 else {
1395                                         selsta_draw = 0;
1396                                 }
1397
1398                                 selwidth_draw = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->selend - but->ofs);
1399
1400                                 unsigned int pos = add_attrib(immVertexFormat(), "pos", GL_INT, 2, CONVERT_INT_TO_FLOAT);
1401                                 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
1402
1403                                 immUniformColor4ubv((unsigned char *)wcol->item);
1404                                 immRecti(pos, rect->xmin + selsta_draw,
1405                                          rect->ymin + 2,
1406                                          min_ii(rect->xmin + selwidth_draw, rect->xmax - 2),
1407                                          rect->ymax - 2);
1408
1409                                 immUnbindProgram();
1410                         }
1411                 }
1412
1413                 /* text cursor */
1414                 but_pos_ofs = but->pos;
1415
1416 #ifdef WITH_INPUT_IME
1417                 /* if is ime compositing, move the cursor */
1418                 if (ime_data && ime_data->composite_len && ime_data->cursor_pos != -1) {
1419                         but_pos_ofs += ime_data->cursor_pos;
1420                 }
1421 #endif
1422
1423                 if (but->pos >= but->ofs) {
1424                         int t;
1425                         if (drawstr[0] != 0) {
1426                                 t = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but_pos_ofs - but->ofs);
1427                         }
1428                         else {
1429                                 t = 0;
1430                         }
1431
1432                         unsigned int pos = add_attrib(immVertexFormat(), "pos", GL_INT, 2, CONVERT_INT_TO_FLOAT);
1433                         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
1434
1435                         immUniformColor3f(0.2f, 0.6f, 0.9f);
1436
1437                         tx = rect->xmin + t + 2;
1438                         ty = rect->ymin + 2;
1439
1440                         /* draw cursor */
1441                         immRecti(pos, rect->xmin + t, ty, tx, rect->ymax - 2);
1442
1443                         immUnbindProgram();
1444                 }
1445
1446 #ifdef WITH_INPUT_IME
1447                 if (ime_data && ime_data->composite_len) {
1448                         /* ime cursor following */
1449                         if (but->pos >= but->ofs) {
1450                                 ui_but_ime_reposition(but, tx + 5, ty + 3, false);
1451                         }
1452
1453                         /* composite underline */
1454                         widget_draw_text_ime_underline(fstyle, wcol, but, rect, ime_data, drawstr);
1455                 }
1456 #endif
1457         }
1458         
1459         if (fstyle->kerning == 1)
1460                 BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
1461
1462 #if 0
1463         ui_rasterpos_safe(x, y, but->aspect);
1464         transopts = ui_translate_buttons();
1465 #endif
1466
1467         /* cut string in 2 parts - only for menu entries */
1468         if ((but->block->flag & UI_BLOCK_LOOP) &&
1469             (but->editstr == NULL))
1470         {
1471                 if (but->flag & UI_BUT_HAS_SEP_CHAR) {
1472                         drawstr_right = strrchr(drawstr, UI_SEP_CHAR);
1473                         if (drawstr_right) {
1474                                 drawstr_left_len = (drawstr_right - drawstr);
1475                                 drawstr_right++;
1476                         }
1477                 }
1478         }
1479         
1480 #ifdef USE_NUMBUTS_LR_ALIGN
1481         if (!drawstr_right && ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER) &&
1482             /* if we're editing or multi-drag (fake editing), then use left alignment */
1483             (but->editstr == NULL) && (drawstr == but->drawstr))
1484         {
1485                 drawstr_right = strchr(drawstr + but->ofs, ':');
1486                 if (drawstr_right) {
1487                         drawstr_right++;
1488                         drawstr_left_len = (drawstr_right - drawstr);
1489
1490                         while (*drawstr_right == ' ') {
1491                                 drawstr_right++;
1492                         }
1493                 }
1494                 else {
1495                         /* no prefix, even so use only cpoin */
1496                         drawstr_right = drawstr + but->ofs;
1497                         use_right_only = true;
1498                 }
1499         }
1500 #endif
1501
1502         if (!use_right_only) {
1503                 /* for underline drawing */
1504                 float font_xofs, font_yofs;
1505
1506                 int drawlen = (drawstr_left_len == INT_MAX) ? strlen(drawstr + but->ofs) : (drawstr_left_len - but->ofs);
1507
1508                 if (drawlen > 0) {
1509                         UI_fontstyle_draw_ex(fstyle, rect, drawstr + but->ofs, (unsigned char *)wcol->text,
1510                                              drawlen, &font_xofs, &font_yofs);
1511
1512                         if (but->menu_key != '\0') {
1513                                 char fixedbuf[128];
1514                                 const char *str;
1515
1516                                 BLI_strncpy(fixedbuf, drawstr + but->ofs, min_ii(sizeof(fixedbuf), drawlen));
1517
1518                                 str = strchr(fixedbuf, but->menu_key - 32); /* upper case */
1519                                 if (str == NULL)
1520                                         str = strchr(fixedbuf, but->menu_key);
1521
1522                                 if (str) {
1523                                         int ul_index = -1;
1524                                         float ul_advance;
1525
1526                                         ul_index = (int)(str - fixedbuf);
1527
1528                                         if (fstyle->kerning == 1) {
1529                                                 BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
1530                                         }
1531
1532                                         fixedbuf[ul_index] = '\0';
1533                                         ul_advance = BLF_width(fstyle->uifont_id, fixedbuf, ul_index);
1534
1535                                         BLF_position(fstyle->uifont_id, rect->xmin + font_xofs + ul_advance, rect->ymin + font_yofs, 0.0f);
1536                                         BLF_color4ubv(fstyle->uifont_id, (unsigned char *)wcol->text);
1537                                         BLF_draw(fstyle->uifont_id, "_", 2);
1538
1539                                         if (fstyle->kerning == 1) {
1540                                                 BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
1541                                         }
1542                                 }
1543                         }
1544                 }
1545         }
1546
1547         /* part text right aligned */
1548         if (drawstr_right) {
1549                 fstyle->align = UI_STYLE_TEXT_RIGHT;
1550                 rect->xmax -= UI_TEXT_CLIP_MARGIN;
1551                 UI_fontstyle_draw(fstyle, rect, drawstr_right, (unsigned char *)wcol->text);
1552         }
1553 }
1554
1555 /* draws text and icons for buttons */
1556 static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *but, rcti *rect)
1557 {
1558         const uiButExtraIconType extra_icon_type = ui_but_icon_extra_get(but);
1559         const bool show_menu_icon = ui_but_draw_menu_icon(but);
1560         float alpha = (float)wcol->text[3] / 255.0f;
1561         char password_str[UI_MAX_DRAW_STR];
1562
1563         ui_but_text_password_hide(password_str, but, false);
1564
1565         /* check for button text label */
1566         if (but->type == UI_BTYPE_MENU && (but->flag & UI_BUT_NODE_LINK)) {
1567                 rcti temp = *rect;
1568                 temp.xmin = rect->xmax - BLI_rcti_size_y(rect) - 1;
1569                 widget_draw_icon(but, ICON_LAYER_USED, alpha, &temp, false);
1570         }
1571
1572         /* If there's an icon too (made with uiDefIconTextBut) then draw the icon
1573          * and offset the text label to accommodate it */
1574
1575         /* Big previews with optional text label below */
1576         if (but->flag & UI_BUT_ICON_PREVIEW && ui_block_is_menu(but->block)) {
1577                 const BIFIconID icon = (but->flag & UI_HAS_ICON) ? but->icon + but->iconadd : ICON_NONE;
1578                 int icon_size = BLI_rcti_size_y(rect);
1579                 int text_size = 0;
1580
1581                 /* This is a bit britle, but avoids adding an 'UI_BUT_HAS_LABEL' flag to but... */
1582                 if (icon_size > BLI_rcti_size_x(rect)) {
1583                         /* button is not square, it has extra height for label */
1584                         text_size = UI_UNIT_Y;
1585                         icon_size -= text_size;
1586                 }
1587
1588                 /* draw icon in rect above the space reserved for the label */
1589                 rect->ymin += text_size;
1590                 glEnable(GL_BLEND);
1591                 widget_draw_preview(icon, alpha, rect);
1592                 glDisable(GL_BLEND);
1593
1594                 /* offset rect to draw label in */
1595                 rect->ymin -= text_size;
1596                 rect->ymax -= icon_size;
1597
1598                 /* vertically centering text */
1599                 rect->ymin += UI_UNIT_Y / 2;
1600         }
1601         /* Icons on the left with optional text label on the right */
1602         else if (but->flag & UI_HAS_ICON || show_menu_icon) {
1603                 const BIFIconID icon = (but->flag & UI_HAS_ICON) ? but->icon + but->iconadd : ICON_NONE;
1604                 const float icon_size = ICON_DEFAULT_WIDTH_SCALE;
1605
1606                 /* menu item - add some more padding so menus don't feel cramped. it must
1607                  * be part of the button so that this area is still clickable */
1608                 if (ui_block_is_pie_menu(but->block)) {
1609                         if (but->dt == UI_EMBOSS_RADIAL)
1610                                 rect->xmin += 0.3f * U.widget_unit;
1611                 }
1612                 else if (ui_block_is_menu(but->block))
1613                         rect->xmin += 0.3f * U.widget_unit;
1614
1615                 widget_draw_icon(but, icon, alpha, rect, show_menu_icon);
1616
1617                 rect->xmin += icon_size;
1618                 /* without this menu keybindings will overlap the arrow icon [#38083] */
1619                 if (show_menu_icon) {
1620                         rect->xmax -= icon_size / 2.0f;
1621                 }
1622         }
1623
1624         if (but->editstr || (but->drawflag & UI_BUT_TEXT_LEFT)) {
1625                 rect->xmin += (UI_TEXT_MARGIN_X * U.widget_unit) / but->block->aspect;
1626         }
1627         else if ((but->drawflag & UI_BUT_TEXT_RIGHT)) {
1628                 rect->xmax -= (UI_TEXT_MARGIN_X * U.widget_unit) / but->block->aspect;
1629         }
1630
1631         /* extra icons, e.g. 'x' icon to clear text or icon for eyedropper */
1632         if (extra_icon_type != UI_BUT_ICONEXTRA_NONE) {
1633                 rcti temp = *rect;
1634
1635                 temp.xmin = temp.xmax - (BLI_rcti_size_y(rect) * 1.08f);
1636
1637                 if (extra_icon_type == UI_BUT_ICONEXTRA_CLEAR) {
1638                         widget_draw_icon(but, ICON_PANEL_CLOSE, alpha, &temp, false);
1639                 }
1640                 else if (extra_icon_type == UI_BUT_ICONEXTRA_EYEDROPPER) {
1641                         widget_draw_icon(but, ICON_EYEDROPPER, alpha, &temp, false);
1642                 }
1643                 else {
1644                         BLI_assert(0);
1645                 }
1646
1647                 rect->xmax -= ICON_SIZE_FROM_BUTRECT(rect);
1648         }
1649
1650         /* clip but->drawstr to fit in available space */
1651         if (but->editstr && but->pos >= 0) {
1652                 ui_text_clip_cursor(fstyle, but, rect);
1653         }
1654         else if (but->drawstr[0] == '\0') {
1655                 /* bypass text clipping on icon buttons */
1656                 but->ofs = 0;
1657                 but->strwidth = 0;
1658         }
1659         else if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER)) {
1660                 ui_text_clip_right_label(fstyle, but, rect);
1661         }
1662         else if (but->flag & UI_BUT_HAS_SEP_CHAR) {
1663                 /* Clip middle, but protect in all case right part containing the shortcut, if any. */
1664                 ui_text_clip_middle_protect_right(fstyle, but, rect, UI_SEP_CHAR);
1665         }
1666         else {
1667                 ui_text_clip_middle(fstyle, but, rect);
1668         }
1669
1670         /* always draw text for textbutton cursor */
1671         widget_draw_text(fstyle, wcol, but, rect);
1672
1673         ui_but_text_password_hide(password_str, but, true);
1674 }
1675
1676 #undef UI_TEXT_CLIP_MARGIN
1677
1678
1679 /* *********************** widget types ************************************* */
1680
1681 static struct uiWidgetStateColors wcol_state_colors = {
1682         {115, 190, 76, 255},
1683         {90, 166, 51, 255},
1684         {240, 235, 100, 255},
1685         {215, 211, 75, 255},
1686         {180, 0, 255, 255},
1687         {153, 0, 230, 255},
1688         0.5f, 0.0f
1689 };
1690
1691 static struct uiWidgetColors wcol_num = {
1692         {25, 25, 25, 255},
1693         {180, 180, 180, 255},
1694         {153, 153, 153, 255},
1695         {90, 90, 90, 255},
1696         
1697         {0, 0, 0, 255},
1698         {255, 255, 255, 255},
1699         
1700         1,
1701         -20, 0
1702 };
1703
1704 static struct uiWidgetColors wcol_numslider = {
1705         {25, 25, 25, 255},
1706         {180, 180, 180, 255},
1707         {153, 153, 153, 255},
1708         {128, 128, 128, 255},
1709         
1710         {0, 0, 0, 255},
1711         {255, 255, 255, 255},
1712         
1713         1,
1714         -20, 0
1715 };
1716
1717 static struct uiWidgetColors wcol_text = {
1718         {25, 25, 25, 255},
1719         {153, 153, 153, 255},
1720         {153, 153, 153, 255},
1721         {90, 90, 90, 255},
1722         
1723         {0, 0, 0, 255},
1724         {255, 255, 255, 255},
1725         
1726         1,
1727         0, 25
1728 };
1729
1730 static struct uiWidgetColors wcol_option = {
1731         {0, 0, 0, 255},
1732         {70, 70, 70, 255},
1733         {70, 70, 70, 255},
1734         {255, 255, 255, 255},
1735         
1736         {0, 0, 0, 255},
1737         {255, 255, 255, 255},
1738         
1739         1,
1740         15, -15
1741 };
1742
1743 /* button that shows popup */
1744 static struct uiWidgetColors wcol_menu = {
1745         {0, 0, 0, 255},
1746         {70, 70, 70, 255},
1747         {70, 70, 70, 255},
1748         {255, 255, 255, 255},
1749         
1750         {255, 255, 255, 255},
1751         {204, 204, 204, 255},
1752         
1753         1,
1754         15, -15
1755 };
1756
1757 /* button that starts pulldown */
1758 static struct uiWidgetColors wcol_pulldown = {
1759         {0, 0, 0, 255},
1760         {63, 63, 63, 255},
1761         {86, 128, 194, 255},
1762         {255, 255, 255, 255},
1763         
1764         {0, 0, 0, 255},
1765         {0, 0, 0, 255},
1766         
1767         0,
1768         25, -20
1769 };
1770
1771 /* button inside menu */
1772 static struct uiWidgetColors wcol_menu_item = {
1773         {0, 0, 0, 255},
1774         {0, 0, 0, 0},
1775         {86, 128, 194, 255},
1776         {172, 172, 172, 128},
1777         
1778         {255, 255, 255, 255},
1779         {0, 0, 0, 255},
1780         
1781         1,
1782         38, 0
1783 };
1784
1785 /* backdrop menu + title text color */
1786 static struct uiWidgetColors wcol_menu_back = {
1787         {0, 0, 0, 255},
1788         {25, 25, 25, 230},
1789         {45, 45, 45, 230},
1790         {100, 100, 100, 255},
1791         
1792         {160, 160, 160, 255},
1793         {255, 255, 255, 255},
1794         
1795         0,
1796         25, -20
1797 };
1798
1799 /* pie menus */
1800 static struct uiWidgetColors wcol_pie_menu = {
1801         {10, 10, 10, 200},
1802         {25, 25, 25, 230},
1803         {140, 140, 140, 255},
1804         {45, 45, 45, 230},
1805
1806         {160, 160, 160, 255},
1807         {255, 255, 255, 255},
1808
1809         1,
1810         10, -10
1811 };
1812
1813
1814 /* tooltip color */
1815 static struct uiWidgetColors wcol_tooltip = {
1816         {0, 0, 0, 255},
1817         {25, 25, 25, 230},
1818         {45, 45, 45, 230},
1819         {100, 100, 100, 255},
1820
1821         {255, 255, 255, 255},
1822         {255, 255, 255, 255},
1823
1824         0,
1825         25, -20
1826 };
1827
1828 static struct uiWidgetColors wcol_radio = {
1829         {0, 0, 0, 255},
1830         {70, 70, 70, 255},
1831         {86, 128, 194, 255},
1832         {255, 255, 255, 255},
1833         
1834         {255, 255, 255, 255},
1835         {0, 0, 0, 255},
1836         
1837         1,
1838         15, -15
1839 };
1840
1841 static struct uiWidgetColors wcol_regular = {
1842         {25, 25, 25, 255},
1843         {153, 153, 153, 255},
1844         {100, 100, 100, 255},
1845         {25, 25, 25, 255},
1846         
1847         {0, 0, 0, 255},
1848         {255, 255, 255, 255},
1849         
1850         0,
1851         0, 0
1852 };
1853
1854 static struct uiWidgetColors wcol_tool = {
1855         {25, 25, 25, 255},
1856         {153, 153, 153, 255},
1857         {100, 100, 100, 255},
1858         {25, 25, 25, 255},
1859         
1860         {0, 0, 0, 255},
1861         {255, 255, 255, 255},
1862         
1863         1,
1864         15, -15
1865 };
1866
1867 static struct uiWidgetColors wcol_box = {
1868         {25, 25, 25, 255},
1869         {128, 128, 128, 255},
1870         {100, 100, 100, 255},
1871         {25, 25, 25, 255},
1872         
1873         {0, 0, 0, 255},
1874         {255, 255, 255, 255},
1875         
1876         0,
1877         0, 0
1878 };
1879
1880 static struct uiWidgetColors wcol_toggle = {
1881         {25, 25, 25, 255},
1882         {153, 153, 153, 255},
1883         {100, 100, 100, 255},
1884         {25, 25, 25, 255},
1885         
1886         {0, 0, 0, 255},
1887         {255, 255, 255, 255},
1888         
1889         0,
1890         0, 0
1891 };
1892
1893 static struct uiWidgetColors wcol_scroll = {
1894         {50, 50, 50, 180},
1895         {80, 80, 80, 180},
1896         {100, 100, 100, 180},
1897         {128, 128, 128, 255},
1898         
1899         {0, 0, 0, 255},
1900         {255, 255, 255, 255},
1901         
1902         1,
1903         5, -5
1904 };
1905
1906 static struct uiWidgetColors wcol_progress = {
1907         {0, 0, 0, 255},
1908         {190, 190, 190, 255},
1909         {100, 100, 100, 180},
1910         {128, 128, 128, 255},
1911         
1912         {0, 0, 0, 255},
1913         {255, 255, 255, 255},
1914         
1915         0,
1916         0, 0
1917 };
1918
1919 static struct uiWidgetColors wcol_list_item = {
1920         {0, 0, 0, 255},
1921         {0, 0, 0, 0},
1922         {86, 128, 194, 255},
1923         {90, 90, 90, 255},
1924         
1925         {0, 0, 0, 255},
1926         {255, 255, 255, 255},
1927         
1928         0,
1929         0, 0
1930 };
1931
1932 /* free wcol struct to play with */
1933 static struct uiWidgetColors wcol_tmp = {
1934         {0, 0, 0, 255},
1935         {128, 128, 128, 255},
1936         {100, 100, 100, 255},
1937         {25, 25, 25, 255},
1938         
1939         {0, 0, 0, 255},
1940         {255, 255, 255, 255},
1941         
1942         0,
1943         0, 0
1944 };
1945
1946
1947 /* called for theme init (new theme) and versions */
1948 void ui_widget_color_init(ThemeUI *tui)
1949 {
1950         tui->wcol_regular = wcol_regular;
1951         tui->wcol_tool = wcol_tool;
1952         tui->wcol_text = wcol_text;
1953         tui->wcol_radio = wcol_radio;
1954         tui->wcol_option = wcol_option;
1955         tui->wcol_toggle = wcol_toggle;
1956         tui->wcol_num = wcol_num;
1957         tui->wcol_numslider = wcol_numslider;
1958         tui->wcol_menu = wcol_menu;
1959         tui->wcol_pulldown = wcol_pulldown;
1960         tui->wcol_menu_back = wcol_menu_back;
1961         tui->wcol_pie_menu = wcol_pie_menu;
1962         tui->wcol_tooltip = wcol_tooltip;
1963         tui->wcol_menu_item = wcol_menu_item;
1964         tui->wcol_box = wcol_box;
1965         tui->wcol_scroll = wcol_scroll;
1966         tui->wcol_list_item = wcol_list_item;
1967         tui->wcol_progress = wcol_progress;
1968
1969         tui->wcol_state = wcol_state_colors;
1970 }
1971
1972 /* ************ button callbacks, state ***************** */
1973
1974 static void widget_state_blend(char cp[3], const char cpstate[3], const float fac)
1975 {
1976         if (fac != 0.0f) {
1977                 cp[0] = (int)((1.0f - fac) * cp[0] + fac * cpstate[0]);
1978                 cp[1] = (int)((1.0f - fac) * cp[1] + fac * cpstate[1]);
1979                 cp[2] = (int)((1.0f - fac) * cp[2] + fac * cpstate[2]);
1980         }
1981 }
1982
1983 /* put all widget colors on half alpha, use local storage */
1984 static void ui_widget_color_disabled(uiWidgetType *wt)
1985 {
1986         static uiWidgetColors wcol_theme_s;
1987
1988         wcol_theme_s = *wt->wcol_theme;
1989
1990         wcol_theme_s.outline[3] *= 0.5;
1991         wcol_theme_s.inner[3] *= 0.5;
1992         wcol_theme_s.inner_sel[3] *= 0.5;
1993         wcol_theme_s.item[3] *= 0.5;
1994         wcol_theme_s.text[3] *= 0.5;
1995         wcol_theme_s.text_sel[3] *= 0.5;
1996
1997         wt->wcol_theme = &wcol_theme_s;
1998 }
1999
2000 /* copy colors from theme, and set changes in it based on state */
2001 static void widget_state(uiWidgetType *wt, int state)
2002 {
2003         uiWidgetStateColors *wcol_state = wt->wcol_state;
2004
2005         if ((state & UI_BUT_LIST_ITEM) && !(state & UI_TEXTINPUT)) {
2006                 /* Override default widget's colors. */
2007                 bTheme *btheme = UI_GetTheme();
2008                 wt->wcol_theme = &btheme->tui.wcol_list_item;
2009
2010                 if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) {
2011                         ui_widget_color_disabled(wt);
2012                 }
2013         }
2014
2015         wt->wcol = *(wt->wcol_theme);
2016
2017         if (state & UI_SELECT) {
2018                 copy_v4_v4_char(wt->wcol.inner, wt->wcol.inner_sel);
2019
2020                 if (state & UI_BUT_ANIMATED_KEY)
2021                         widget_state_blend(wt->wcol.inner, wcol_state->inner_key_sel, wcol_state->blend);
2022                 else if (state & UI_BUT_ANIMATED)
2023                         widget_state_blend(wt->wcol.inner, wcol_state->inner_anim_sel, wcol_state->blend);
2024                 else if (state & UI_BUT_DRIVEN)
2025                         widget_state_blend(wt->wcol.inner, wcol_state->inner_driven_sel, wcol_state->blend);
2026
2027                 copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel);
2028                 
2029                 if (state & UI_SELECT)
2030                         SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown);
2031         }
2032         else {
2033                 if (state & UI_BUT_ANIMATED_KEY)
2034                         widget_state_blend(wt->wcol.inner, wcol_state->inner_key, wcol_state->blend);
2035                 else if (state & UI_BUT_ANIMATED)
2036                         widget_state_blend(wt->wcol.inner, wcol_state->inner_anim, wcol_state->blend);
2037                 else if (state & UI_BUT_DRIVEN)
2038                         widget_state_blend(wt->wcol.inner, wcol_state->inner_driven, wcol_state->blend);
2039
2040                 if (state & UI_ACTIVE) { /* mouse over? */
2041                         wt->wcol.inner[0] = wt->wcol.inner[0] >= 240 ? 255 : wt->wcol.inner[0] + 15;
2042                         wt->wcol.inner[1] = wt->wcol.inner[1] >= 240 ? 255 : wt->wcol.inner[1] + 15;
2043                         wt->wcol.inner[2] = wt->wcol.inner[2] >= 240 ? 255 : wt->wcol.inner[2] + 15;
2044                 }
2045         }
2046
2047         if (state & UI_BUT_REDALERT) {
2048                 char red[4] = {255, 0, 0};
2049                 widget_state_blend(wt->wcol.inner, red, 0.4f);
2050         }
2051
2052         if (state & UI_BUT_DRAG_MULTI) {
2053                 /* the button isn't SELECT but we're editing this so draw with sel color */
2054                 copy_v4_v4_char(wt->wcol.inner, wt->wcol.inner_sel);
2055                 SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown);
2056                 widget_state_blend(wt->wcol.text, wt->wcol.text_sel, 0.85f);
2057         }
2058
2059         if (state & UI_BUT_NODE_ACTIVE) {
2060                 char blue[4] = {86, 128, 194};
2061                 widget_state_blend(wt->wcol.inner, blue, 0.3f);
2062         }
2063 }
2064
2065 /* sliders use special hack which sets 'item' as inner when drawing filling */
2066 static void widget_state_numslider(uiWidgetType *wt, int state)
2067 {
2068         uiWidgetStateColors *wcol_state = wt->wcol_state;
2069         float blend = wcol_state->blend - 0.2f; /* XXX special tweak to make sure that bar will still be visible */
2070
2071         /* call this for option button */
2072         widget_state(wt, state);
2073         
2074         /* now, set the inner-part so that it reflects state settings too */
2075         /* TODO: maybe we should have separate settings for the blending colors used for this case? */
2076         if (state & UI_SELECT) {
2077                 
2078                 if (state & UI_BUT_ANIMATED_KEY)
2079                         widget_state_blend(wt->wcol.item, wcol_state->inner_key_sel, blend);
2080                 else if (state & UI_BUT_ANIMATED)
2081                         widget_state_blend(wt->wcol.item, wcol_state->inner_anim_sel, blend);
2082                 else if (state & UI_BUT_DRIVEN)
2083                         widget_state_blend(wt->wcol.item, wcol_state->inner_driven_sel, blend);
2084                 
2085                 if (state & UI_SELECT)
2086                         SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown);
2087         }
2088         else {
2089                 if (state & UI_BUT_ANIMATED_KEY)
2090                         widget_state_blend(wt->wcol.item, wcol_state->inner_key, blend);
2091                 else if (state & UI_BUT_ANIMATED)
2092                         widget_state_blend(wt->wcol.item, wcol_state->inner_anim, blend);
2093                 else if (state & UI_BUT_DRIVEN)
2094                         widget_state_blend(wt->wcol.item, wcol_state->inner_driven, blend);
2095         }
2096 }
2097
2098 /* labels use theme colors for text */
2099 static void widget_state_option_menu(uiWidgetType *wt, int state)
2100 {
2101         bTheme *btheme = UI_GetTheme(); /* XXX */
2102         
2103         /* call this for option button */
2104         widget_state(wt, state);
2105         
2106         /* if not selected we get theme from menu back */
2107         if (state & UI_SELECT)
2108                 copy_v3_v3_char(wt->wcol.text, btheme->tui.wcol_menu_back.text_sel);
2109         else
2110                 copy_v3_v3_char(wt->wcol.text, btheme->tui.wcol_menu_back.text);
2111 }
2112
2113
2114 static void widget_state_nothing(uiWidgetType *wt, int UNUSED(state))
2115 {
2116         wt->wcol = *(wt->wcol_theme);
2117 }       
2118
2119 /* special case, button that calls pulldown */
2120 static void widget_state_pulldown(uiWidgetType *wt, int state)
2121 {
2122         wt->wcol = *(wt->wcol_theme);
2123         
2124         copy_v4_v4_char(wt->wcol.inner, wt->wcol.inner_sel);
2125         copy_v3_v3_char(wt->wcol.outline, wt->wcol.inner);
2126
2127         if (state & UI_ACTIVE)
2128                 copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel);
2129 }
2130
2131 /* special case, pie menu items */
2132 static void widget_state_pie_menu_item(uiWidgetType *wt, int state)
2133 {
2134         wt->wcol = *(wt->wcol_theme);
2135
2136         /* active and disabled (not so common) */
2137         if ((state & UI_BUT_DISABLED) && (state & UI_ACTIVE)) {
2138                 widget_state_blend(wt->wcol.text, wt->wcol.text_sel, 0.5f);
2139                 /* draw the backdrop at low alpha, helps navigating with keys
2140                  * when disabled items are active */
2141                 copy_v4_v4_char(wt->wcol.inner, wt->wcol.item);
2142                 wt->wcol.inner[3] = 64;
2143         }
2144         /* regular disabled */
2145         else if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) {
2146                 widget_state_blend(wt->wcol.text, wt->wcol.inner, 0.5f);
2147         }
2148         /* regular active */
2149         else if (state & UI_SELECT) {
2150                 copy_v4_v4_char(wt->wcol.outline, wt->wcol.inner_sel);
2151                 copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel);
2152         }
2153         else if (state & UI_ACTIVE) {
2154                 copy_v4_v4_char(wt->wcol.inner, wt->wcol.item);
2155                 copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel);
2156         }
2157 }
2158
2159 /* special case, menu items */
2160 static void widget_state_menu_item(uiWidgetType *wt, int state)
2161 {
2162         wt->wcol = *(wt->wcol_theme);
2163         
2164         /* active and disabled (not so common) */
2165         if ((state & UI_BUT_DISABLED) && (state & UI_ACTIVE)) {
2166                 widget_state_blend(wt->wcol.text, wt->wcol.text_sel, 0.5f);
2167                 /* draw the backdrop at low alpha, helps navigating with keys
2168                  * when disabled items are active */
2169                 copy_v4_v4_char(wt->wcol.inner, wt->wcol.inner_sel);
2170                 wt->wcol.inner[3] = 64;
2171         }
2172         /* regular disabled */
2173         else if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) {
2174                 widget_state_blend(wt->wcol.text, wt->wcol.inner, 0.5f);
2175         }
2176         /* regular active */
2177         else if (state & UI_ACTIVE) {
2178                 copy_v4_v4_char(wt->wcol.inner, wt->wcol.inner_sel);
2179                 copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel);
2180         }
2181 }
2182
2183
2184 /* ************ menu backdrop ************************* */
2185
2186 /* outside of rect, rad to left/bottom/right */
2187 static void widget_softshadow(const rcti *rect, int roundboxalign, const float radin)
2188 {
2189         bTheme *btheme = UI_GetTheme();
2190         uiWidgetBase wtb;
2191         rcti rect1 = *rect;
2192         float alphastep;
2193         int step, totvert;
2194         float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2];
2195         const float radout = UI_ThemeMenuShadowWidth();
2196         
2197         /* disabled shadow */
2198         if (radout == 0.0f)
2199                 return;
2200         
2201         /* prevent tooltips to not show round shadow */
2202         if (radout > 0.2f * BLI_rcti_size_y(&rect1))
2203                 rect1.ymax -= 0.2f * BLI_rcti_size_y(&rect1);
2204         else
2205                 rect1.ymax -= radout;
2206         
2207         /* inner part */
2208         totvert = round_box_shadow_edges(wtb.inner_v, &rect1, radin, roundboxalign & (UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT), 0.0f);
2209
2210         /* we draw a number of increasing size alpha quad strips */
2211         alphastep = 3.0f * btheme->tui.menu_shadow_fac / radout;
2212
2213         unsigned int pos = add_attrib(immVertexFormat(), "pos", GL_FLOAT, 2, KEEP_FLOAT);
2214
2215         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2216
2217         for (step = 1; step <= (int)radout; step++) {
2218                 float expfac = sqrtf(step / radout);
2219                 
2220                 round_box_shadow_edges(wtb.outer_v, &rect1, radin, UI_CNR_ALL, (float)step);
2221                 
2222                 immUniformColor4f(0.0f, 0.0f, 0.0f, alphastep * (1.0f - expfac));
2223
2224                 widget_verts_to_triangle_strip(&wtb, totvert, triangle_strip);
2225
2226                 widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_STRIP, triangle_strip, NULL, totvert * 2);
2227         }
2228
2229         immUnbindProgram();
2230 }
2231
2232 static void widget_menu_back(uiWidgetColors *wcol, rcti *rect, int flag, int direction)
2233 {
2234         uiWidgetBase wtb;
2235         int roundboxalign = UI_CNR_ALL;
2236         
2237         widget_init(&wtb);
2238         
2239         /* menu is 2nd level or deeper */
2240         if (flag & UI_BLOCK_POPUP) {
2241                 //rect->ymin -= 4.0;
2242                 //rect->ymax += 4.0;
2243         }
2244         else if (direction == UI_DIR_DOWN) {
2245                 roundboxalign = (UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT);
2246                 rect->ymin -= 0.1f * U.widget_unit;
2247         }
2248         else if (direction == UI_DIR_UP) {
2249                 roundboxalign = UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT;
2250                 rect->ymax += 0.1f * U.widget_unit;
2251         }
2252         
2253         glEnable(GL_BLEND);
2254         widget_softshadow(rect, roundboxalign, 0.25f * U.widget_unit);
2255         
2256         round_box_edges(&wtb, roundboxalign, rect, 0.25f * U.widget_unit);
2257         wtb.draw_emboss = false;
2258         widgetbase_draw(&wtb, wcol);
2259         
2260         glDisable(GL_BLEND);
2261 }
2262
2263
2264 static void ui_hsv_cursor(float x, float y)
2265 {
2266         unsigned int pos = add_attrib(immVertexFormat(), "pos", GL_FLOAT, 2, KEEP_FLOAT);
2267
2268         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2269
2270         immUniformColor3f(1.0f, 1.0f, 1.0f);
2271         imm_draw_filled_circle(pos, x, y, 3.0f * U.pixelsize, 8);
2272         
2273         glEnable(GL_BLEND);
2274         glEnable(GL_LINE_SMOOTH);
2275         immUniformColor3f(0.0f, 0.0f, 0.0f);
2276         imm_draw_lined_circle(pos, x, y, 3.0f * U.pixelsize, 12);
2277         glDisable(GL_BLEND);
2278         glDisable(GL_LINE_SMOOTH);
2279
2280         immUnbindProgram();
2281 }
2282
2283 void ui_hsvcircle_vals_from_pos(float *val_rad, float *val_dist, const rcti *rect,
2284                                 const float mx, const float my)
2285 {
2286         /* duplication of code... well, simple is better now */
2287         const float centx = BLI_rcti_cent_x_fl(rect);
2288         const float centy = BLI_rcti_cent_y_fl(rect);
2289         const float radius = (float)min_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)) / 2.0f;
2290         const float m_delta[2] = {mx - centx, my - centy};
2291         const float dist_sq = len_squared_v2(m_delta);
2292
2293         *val_dist = (dist_sq < (radius * radius)) ? sqrtf(dist_sq) / radius : 1.0f;
2294         *val_rad = atan2f(m_delta[0], m_delta[1]) / (2.0f * (float)M_PI) + 0.5f;
2295 }
2296
2297 /* cursor in hsv circle, in float units -1 to 1, to map on radius */
2298 void ui_hsvcircle_pos_from_vals(uiBut *but, const rcti *rect, float *hsv, float *xpos, float *ypos)
2299 {
2300         /* duplication of code... well, simple is better now */
2301         const float centx = BLI_rcti_cent_x_fl(rect);
2302         const float centy = BLI_rcti_cent_y_fl(rect);
2303         float radius = (float)min_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)) / 2.0f;
2304         float ang, radius_t;
2305         
2306         ang = 2.0f * (float)M_PI * hsv[0] + (float)M_PI_2;
2307         
2308         if ((but->flag & UI_BUT_COLOR_CUBIC) && (U.color_picker_type == USER_CP_CIRCLE_HSV))
2309                 radius_t = (1.0f - pow3f(1.0f - hsv[1]));
2310         else
2311                 radius_t = hsv[1];
2312         
2313         radius = CLAMPIS(radius_t, 0.0f, 1.0f) * radius;
2314         *xpos = centx + cosf(-ang) * radius;
2315         *ypos = centy + sinf(-ang) * radius;
2316 }
2317
2318 static void ui_draw_but_HSVCIRCLE(uiBut *but, uiWidgetColors *wcol, const rcti *rect)
2319 {
2320         /* TODO(merwin): reimplement as shader for pixel-perfect colors */
2321
2322         const int tot = 64;
2323         const float radstep = 2.0f * (float)M_PI / (float)tot;
2324         const float centx = BLI_rcti_cent_x_fl(rect);
2325         const float centy = BLI_rcti_cent_y_fl(rect);
2326         float radius = (float)min_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)) / 2.0f;
2327
2328         ColorPicker *cpicker = but->custom_data;
2329         const float *hsv_ptr = cpicker->color_data;
2330         float rgb[3], hsvo[3], hsv[3], col[3], colcent[3];
2331         bool color_profile = ui_but_is_colorpicker_display_space(but);
2332                 
2333         /* color */
2334         ui_but_v3_get(but, rgb);
2335
2336         /* since we use compat functions on both 'hsv' and 'hsvo', they need to be initialized */
2337         hsvo[0] = hsv[0] = hsv_ptr[0];
2338         hsvo[1] = hsv[1] = hsv_ptr[1];
2339         hsvo[2] = hsv[2] = hsv_ptr[2];
2340
2341         if (color_profile)
2342                 ui_block_cm_to_display_space_v3(but->block, rgb);
2343
2344         ui_rgb_to_color_picker_compat_v(rgb, hsv);
2345         copy_v3_v3(hsvo, hsv);
2346
2347         CLAMP(hsv[2], 0.0f, 1.0f); /* for display only */
2348
2349         /* exception: if 'lock' is set
2350          * lock the value of the color wheel to 1.
2351          * Useful for color correction tools where you're only interested in hue. */
2352         if (but->flag & UI_BUT_COLOR_LOCK) {
2353                 if (U.color_picker_type == USER_CP_CIRCLE_HSV)
2354                         hsv[2] = 1.0f;
2355                 else
2356                         hsv[2] = 0.5f;
2357         }
2358         
2359         ui_color_picker_to_rgb(0.0f, 0.0f, hsv[2], colcent, colcent + 1, colcent + 2);
2360
2361         VertexFormat *format = immVertexFormat();
2362         unsigned int pos = add_attrib(format, "pos", GL_FLOAT, 2, KEEP_FLOAT);
2363         unsigned int color = add_attrib(format, "color", GL_FLOAT, 3, KEEP_FLOAT);
2364
2365         immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
2366
2367         immBegin(GL_TRIANGLE_FAN, tot + 2);
2368         immAttrib3fv(color, colcent);
2369         immVertex2f(pos, centx, centy);
2370         
2371         float ang = 0.0f;
2372         for (int a = 0; a <= tot; a++, ang += radstep) {
2373                 float si = sinf(ang);
2374                 float co = cosf(ang);
2375                 
2376                 ui_hsvcircle_vals_from_pos(hsv, hsv + 1, rect, centx + co * radius, centy + si * radius);
2377
2378                 ui_color_picker_to_rgb_v(hsv, col);
2379
2380                 immAttrib3fv(color, col);
2381                 immVertex2f(pos, centx + co * radius, centy + si * radius);
2382         }
2383         immEnd();
2384         immUnbindProgram();
2385
2386         /* fully rounded outline */
2387         format = immVertexFormat();
2388         pos = add_attrib(format, "pos", GL_FLOAT, 2, KEEP_FLOAT);
2389
2390         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2391
2392         glEnable(GL_BLEND);
2393         glEnable(GL_LINE_SMOOTH);
2394
2395         immUniformColor3ubv((unsigned char *)wcol->outline);
2396         imm_draw_lined_circle(pos, centx, centy, radius, tot);
2397
2398         immUnbindProgram();
2399
2400         glDisable(GL_BLEND);
2401         glDisable(GL_LINE_SMOOTH);
2402
2403         /* cursor */
2404         float xpos, ypos;
2405         ui_hsvcircle_pos_from_vals(but, rect, hsvo, &xpos, &ypos);
2406         ui_hsv_cursor(xpos, ypos);
2407 }
2408
2409 /* ************ custom buttons, old stuff ************** */
2410
2411 /* draws in resolution of 48x4 colors */
2412 void ui_draw_gradient(const rcti *rect, const float hsv[3], const int type, const float alpha)
2413 {
2414         /* allows for 4 steps (red->yellow) */
2415         const int steps = 48;
2416         const float color_step = 1.0f / steps;
2417         int a;
2418         float h = hsv[0], s = hsv[1], v = hsv[2];
2419         float dx, dy, sx1, sx2, sy;
2420         float col0[4][3];   /* left half, rect bottom to top */
2421         float col1[4][3];   /* right half, rect bottom to top */
2422
2423         /* draw series of gouraud rects */
2424         
2425         switch (type) {
2426                 case UI_GRAD_SV:
2427                         hsv_to_rgb(h, 0.0, 0.0,   &col1[0][0], &col1[0][1], &col1[0][2]);
2428                         hsv_to_rgb(h, 0.0, 0.333, &col1[1][0], &col1[1][1], &col1[1][2]);
2429                         hsv_to_rgb(h, 0.0, 0.666, &col1[2][0], &col1[2][1], &col1[2][2]);
2430                         hsv_to_rgb(h, 0.0, 1.0,   &col1[3][0], &col1[3][1], &col1[3][2]);
2431                         break;
2432                 case UI_GRAD_HV:
2433                         hsv_to_rgb(0.0, s, 0.0,   &col1[0][0], &col1[0][1], &col1[0][2]);
2434                         hsv_to_rgb(0.0, s, 0.333, &col1[1][0], &col1[1][1], &col1[1][2]);
2435                         hsv_to_rgb(0.0, s, 0.666, &col1[2][0], &col1[2][1], &col1[2][2]);
2436                         hsv_to_rgb(0.0, s, 1.0,   &col1[3][0], &col1[3][1], &col1[3][2]);
2437                         break;
2438                 case UI_GRAD_HS:
2439                         hsv_to_rgb(0.0, 0.0, v,   &col1[0][0], &col1[0][1], &col1[0][2]);
2440                         hsv_to_rgb(0.0, 0.333, v, &col1[1][0], &col1[1][1], &col1[1][2]);
2441                         hsv_to_rgb(0.0, 0.666, v, &col1[2][0], &col1[2][1], &col1[2][2]);
2442                         hsv_to_rgb(0.0, 1.0, v,   &col1[3][0], &col1[3][1], &col1[3][2]);
2443                         break;
2444                 case UI_GRAD_H:
2445                         hsv_to_rgb(0.0, 1.0, 1.0, &col1[0][0], &col1[0][1], &col1[0][2]);
2446                         copy_v3_v3(col1[1], col1[0]);
2447                         copy_v3_v3(col1[2], col1[0]);
2448                         copy_v3_v3(col1[3], col1[0]);
2449                         break;
2450                 case UI_GRAD_S:
2451                         hsv_to_rgb(1.0, 0.0, 1.0, &col1[1][0], &col1[1][1], &col1[1][2]);
2452                         copy_v3_v3(col1[0], col1[1]);
2453                         copy_v3_v3(col1[2], col1[1]);
2454                         copy_v3_v3(col1[3], col1[1]);
2455                         break;
2456                 case UI_GRAD_V:
2457                         hsv_to_rgb(1.0, 1.0, 0.0, &col1[2][0], &col1[2][1], &col1[2][2]);
2458                         copy_v3_v3(col1[0], col1[2]);
2459                         copy_v3_v3(col1[1], col1[2]);
2460                         copy_v3_v3(col1[3], col1[2]);
2461                         break;
2462                 default:
2463                         assert(!"invalid 'type' argument");
2464                         hsv_to_rgb(1.0, 1.0, 1.0, &col1[2][0], &col1[2][1], &col1[2][2]);
2465                         copy_v3_v3(col1[0], col1[2]);
2466                         copy_v3_v3(col1[1], col1[2]);
2467                         copy_v3_v3(col1[3], col1[2]);
2468                         break;
2469         }
2470
2471         /* old below */
2472         VertexFormat *format = immVertexFormat();
2473         unsigned int pos = add_attrib(format, "pos", GL_FLOAT, 2, KEEP_FLOAT);
2474         unsigned int col = add_attrib(format, "color", GL_FLOAT, 4, KEEP_FLOAT);
2475         immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
2476         
2477         immBegin(GL_TRIANGLES, steps * 3 * 6);
2478         for (dx = 0.0f; dx < 0.999f; dx += color_step) { /* 0.999 = prevent float inaccuracy for steps */
2479                 const float dx_next = dx + color_step;
2480
2481                 /* previous color */
2482                 copy_v3_v3(col0[0], col1[0]);
2483                 copy_v3_v3(col0[1], col1[1]);
2484                 copy_v3_v3(col0[2], col1[2]);
2485                 copy_v3_v3(col0[3], col1[3]);
2486                 
2487                 /* new color */
2488                 switch (type) {
2489                         case UI_GRAD_SV:
2490                                 hsv_to_rgb(h, dx, 0.0,   &col1[0][0], &col1[0][1], &col1[0][2]);
2491                                 hsv_to_rgb(h, dx, 0.333, &col1[1][0], &col1[1][1], &col1[1][2]);
2492                                 hsv_to_rgb(h, dx, 0.666, &col1[2][0], &col1[2][1], &col1[2][2]);
2493                                 hsv_to_rgb(h, dx, 1.0,   &col1[3][0], &col1[3][1], &col1[3][2]);
2494                                 break;
2495                         case UI_GRAD_HV:
2496                                 hsv_to_rgb(dx_next, s, 0.0,   &col1[0][0], &col1[0][1], &col1[0][2]);
2497                                 hsv_to_rgb(dx_next, s, 0.333, &col1[1][0], &col1[1][1], &col1[1][2]);
2498                                 hsv_to_rgb(dx_next, s, 0.666, &col1[2][0], &col1[2][1], &col1[2][2]);
2499                                 hsv_to_rgb(dx_next, s, 1.0,   &col1[3][0], &col1[3][1], &col1[3][2]);
2500                                 break;
2501                         case UI_GRAD_HS:
2502                                 hsv_to_rgb(dx_next, 0.0, v,   &col1[0][0], &col1[0][1], &col1[0][2]);
2503                                 hsv_to_rgb(dx_next, 0.333, v, &col1[1][0], &col1[1][1], &col1[1][2]);
2504                                 hsv_to_rgb(dx_next, 0.666, v, &col1[2][0], &col1[2][1], &col1[2][2]);
2505                                 hsv_to_rgb(dx_next, 1.0, v,   &col1[3][0], &col1[3][1], &col1[3][2]);
2506                                 break;
2507                         case UI_GRAD_H:
2508                                 /* annoying but without this the color shifts - could be solved some other way
2509                                  * - campbell */
2510                                 hsv_to_rgb(dx_next, 1.0, 1.0, &col1[0][0], &col1[0][1], &col1[0][2]);
2511                                 copy_v3_v3(col1[1], col1[0]);
2512                                 copy_v3_v3(col1[2], col1[0]);
2513                                 copy_v3_v3(col1[3], col1[0]);
2514                                 break;
2515                         case UI_GRAD_S:
2516                                 hsv_to_rgb(h, dx, 1.0, &col1[1][0], &col1[1][1], &col1[1][2]);
2517                                 copy_v3_v3(col1[0], col1[1]);
2518                                 copy_v3_v3(col1[2], col1[1]);
2519                                 copy_v3_v3(col1[3], col1[1]);
2520                                 break;
2521                         case UI_GRAD_V:
2522                                 hsv_to_rgb(h, 1.0, dx, &col1[2][0], &col1[2][1], &col1[2][2]);
2523                                 copy_v3_v3(col1[0], col1[2]);
2524                                 copy_v3_v3(col1[1], col1[2]);
2525                                 copy_v3_v3(col1[3], col1[2]);
2526                                 break;
2527                 }
2528                 
2529                 /* rect */
2530                 sx1 = rect->xmin + dx      * BLI_rcti_size_x(rect);
2531                 sx2 = rect->xmin + dx_next * BLI_rcti_size_x(rect);
2532                 sy = rect->ymin;
2533                 dy = (float)BLI_rcti_size_y(rect) / 3.0f;
2534                 
2535                 for (a = 0; a < 3; a++, sy += dy) {
2536                         immAttrib4f(col, col0[a][0], col0[a][1], col0[a][2], alpha);
2537                         immVertex2f(pos, sx1, sy);
2538                         
2539                         immAttrib4f(col, col1[a][0], col1[a][1], col1[a][2], alpha);
2540                         immVertex2f(pos, sx2, sy);
2541
2542                         immAttrib4f(col, col1[a + 1][0], col1[a + 1][1], col1[a + 1][2], alpha);
2543                         immVertex2f(pos, sx2, sy + dy);
2544
2545                         immAttrib4f(col, col0[a][0], col0[a][1], col0[a][2], alpha);
2546                         immVertex2f(pos, sx1, sy);
2547
2548                         immAttrib4f(col, col1[a + 1][0], col1[a + 1][1], col1[a + 1][2], alpha);
2549                         immVertex2f(pos, sx2, sy + dy);
2550                         
2551                         immAttrib4f(col, col0[a + 1][0], col0[a + 1][1], col0[a + 1][2], alpha);
2552                         immVertex2f(pos, sx1, sy + dy);
2553                 }
2554         }
2555         immEnd();
2556
2557         immUnbindProgram();
2558 }
2559
2560 bool ui_but_is_colorpicker_display_space(uiBut *but)
2561 {
2562         bool color_profile = but->block->color_profile;
2563
2564         if (but->rnaprop) {
2565                 if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA)
2566                         color_profile = false;
2567         }
2568
2569         return color_profile;
2570 }
2571
2572 void ui_hsvcube_pos_from_vals(uiBut *but, const rcti *rect, float *hsv, float *xp, float *yp)
2573 {
2574         float x = 0.0f, y = 0.0f;
2575
2576         switch ((int)but->a1) {
2577                 case UI_GRAD_SV:
2578                         x = hsv[1]; y = hsv[2]; break;
2579                 case UI_GRAD_HV:
2580                         x = hsv[0]; y = hsv[2]; break;
2581                 case UI_GRAD_HS:
2582                         x = hsv[0]; y = hsv[1]; break;
2583                 case UI_GRAD_H:
2584                         x = hsv[0]; y = 0.5; break;
2585                 case UI_GRAD_S:
2586                         x = hsv[1]; y = 0.5; break;
2587                 case UI_GRAD_V:
2588                         x = hsv[2]; y = 0.5; break;
2589                 case UI_GRAD_L_ALT:
2590                         x = 0.5f;
2591                         /* exception only for value strip - use the range set in but->min/max */
2592                         y = hsv[2];
2593                         break;
2594                 case UI_GRAD_V_ALT:
2595                         x = 0.5f;
2596                         /* exception only for value strip - use the range set in but->min/max */
2597                         y = (hsv[2] - but->softmin) / (but->softmax - but->softmin);
2598                         break;
2599         }
2600
2601         /* cursor */
2602         *xp = rect->xmin + x * BLI_rcti_size_x(rect);
2603         *yp = rect->ymin + y * BLI_rcti_size_y(rect);
2604 }
2605
2606 static void ui_draw_but_HSVCUBE(uiBut *but, const rcti *rect)
2607 {
2608         float rgb[3];
2609         float x = 0.0f, y = 0.0f;
2610         ColorPicker *cpicker = but->custom_data;
2611         float *hsv = cpicker->color_data;
2612         float hsv_n[3];
2613         bool use_display_colorspace = ui_but_is_colorpicker_display_space(but);
2614         
2615         copy_v3_v3(hsv_n, hsv);
2616         
2617         ui_but_v3_get(but, rgb);
2618         
2619         if (use_display_colorspace)
2620                 ui_block_cm_to_display_space_v3(but->block, rgb);
2621         
2622         rgb_to_hsv_compat_v(rgb, hsv_n);
2623         
2624         ui_draw_gradient(rect, hsv_n, but->a1, 1.0f);
2625
2626         ui_hsvcube_pos_from_vals(but, rect, hsv_n, &x, &y);
2627         CLAMP(x, rect->xmin + 3.0f, rect->xmax - 3.0f);
2628         CLAMP(y, rect->ymin + 3.0f, rect->ymax - 3.0f);
2629         
2630         ui_hsv_cursor(x, y);
2631         
2632         /* outline */
2633         unsigned int pos = add_attrib(immVertexFormat(), "pos", GL_FLOAT, 2, KEEP_FLOAT);
2634         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2635         immUniformColor3ub(0, 0, 0);
2636         imm_draw_line_box(pos, (rect->xmin), (rect->ymin), (rect->xmax), (rect->ymax));
2637         immUnbindProgram();
2638 }
2639
2640 /* vertical 'value' slider, using new widget code */
2641 static void ui_draw_but_HSV_v(uiBut *but, const rcti *rect)
2642 {
2643         uiWidgetBase wtb;
2644         const float rad = 0.5f * BLI_rcti_size_x(rect);
2645         float x, y;
2646         float rgb[3], hsv[3], v;
2647         bool color_profile = but->block->color_profile;
2648         
2649         if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA)
2650                 color_profile = false;
2651
2652         ui_but_v3_get(but, rgb);
2653
2654         if (color_profile)
2655                 ui_block_cm_to_display_space_v3(but->block, rgb);
2656
2657         if (but->a1 == UI_GRAD_L_ALT)
2658                 rgb_to_hsl_v(rgb, hsv);
2659         else
2660                 rgb_to_hsv_v(rgb, hsv);
2661         v = hsv[2];
2662         
2663         /* map v from property range to [0,1] */
2664         if (but->a1 == UI_GRAD_V_ALT) {
2665                 float min = but->softmin, max = but->softmax;
2666                 if (color_profile) {
2667                         ui_block_cm_to_display_space_range(but->block, &min, &max);
2668                 }
2669                 v = (v - min) / (max - min);
2670         }
2671
2672         widget_init(&wtb);
2673         
2674         /* fully rounded */
2675         round_box_edges(&wtb, UI_CNR_ALL, rect, rad);
2676         
2677         /* setup temp colors */
2678         wcol_tmp.outline[0] = wcol_tmp.outline[1] = wcol_tmp.outline[2] = 0;
2679         wcol_tmp.inner[0] = wcol_tmp.inner[1] = wcol_tmp.inner[2] = 128;
2680         wcol_tmp.shadetop = 127;
2681         wcol_tmp.shadedown = -128;
2682         wcol_tmp.shaded = 1;
2683         
2684         widgetbase_draw(&wtb, &wcol_tmp);
2685
2686         /* cursor */
2687         x = rect->xmin + 0.5f * BLI_rcti_size_x(rect);
2688         y = rect->ymin + v    * BLI_rcti_size_y(rect);
2689         CLAMP(y, rect->ymin + 3.0f, rect->ymax - 3.0f);
2690
2691         ui_hsv_cursor(x, y);
2692 }
2693
2694
2695 /* ************ separator, for menus etc ***************** */
2696 static void ui_draw_separator(const rcti *rect,  uiWidgetColors *wcol)
2697 {
2698         int y = rect->ymin + BLI_rcti_size_y(rect) / 2 - 1;
2699         unsigned char col[4] = {
2700                 wcol->text[0],
2701                 wcol->text[1],
2702                 wcol->text[2],
2703                 30
2704         };
2705
2706         unsigned int pos = add_attrib(immVertexFormat(), "pos", GL_FLOAT, 2, KEEP_FLOAT);
2707         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2708
2709         glEnable(GL_BLEND);
2710         immUniformColor4ubv(col);
2711         glLineWidth(1.0f);
2712
2713         immBegin(GL_LINES, 2);
2714         immVertex2f(pos, rect->xmin, y);
2715         immVertex2f(pos, rect->xmax, y);
2716         immEnd();
2717
2718         glDisable(GL_BLEND);
2719
2720         immUnbindProgram();
2721 }
2722
2723 /* ************ button callbacks, draw ***************** */
2724 static void widget_numbut_draw(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, bool emboss)
2725 {
2726         uiWidgetBase wtb;
2727         const float rad = 0.5f * BLI_rcti_size_y(rect);
2728         float textofs = rad * 0.85f;
2729
2730         if (state & UI_SELECT)
2731                 SWAP(short, wcol->shadetop, wcol->shadedown);
2732         
2733         widget_init(&wtb);
2734         
2735         if (!emboss) {
2736                 round_box_edges(&wtb, roundboxalign, rect, rad);
2737         }
2738
2739         /* decoration */
2740         if (!(state & UI_TEXTINPUT)) {
2741                 widget_num_tria(&wtb.tria1, rect, 0.6f, 'l');
2742                 widget_num_tria(&wtb.tria2, rect, 0.6f, 'r');
2743         }
2744
2745         widgetbase_draw(&wtb, wcol);
2746         
2747         if (!(state & UI_TEXTINPUT)) {
2748                 /* text space */
2749                 rect->xmin += textofs;
2750                 rect->xmax -= textofs;
2751         }
2752 }
2753
2754 static void widget_numbut(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
2755 {
2756         widget_numbut_draw(wcol, rect, state, roundboxalign, false);
2757 }
2758
2759 /**
2760  * Draw number buttons still with triangles when field is not embossed
2761  */
2762 static void widget_numbut_embossn(uiBut *UNUSED(but), uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
2763 {
2764         widget_numbut_draw(wcol, rect, state, roundboxalign, true);
2765 }
2766
2767 bool ui_link_bezier_points(const rcti *rect, float coord_array[][2], int resol)
2768 {
2769         float dist, vec[4][2];
2770
2771         vec[0][0] = rect->xmin;
2772         vec[0][1] = rect->ymin;
2773         vec[3][0] = rect->xmax;
2774         vec[3][1] = rect->ymax;
2775         
2776         dist = 0.5f * fabsf(vec[0][0] - vec[3][0]);
2777         
2778         vec[1][0] = vec[0][0] + dist;
2779         vec[1][1] = vec[0][1];
2780         
2781         vec[2][0] = vec[3][0] - dist;
2782         vec[2][1] = vec[3][1];
2783         
2784         BKE_curve_forward_diff_bezier(vec[0][0], vec[1][0], vec[2][0], vec[3][0], &coord_array[0][0], resol, sizeof(float[2]));
2785         BKE_curve_forward_diff_bezier(vec[0][1], vec[1][1], vec[2][1], vec[3][1], &coord_array[0][1], resol, sizeof(float[2]));
2786
2787         /* TODO: why return anything if always true? */
2788         return true;
2789 }
2790
2791 #define LINK_RESOL  24
2792 void ui_draw_link_bezier(const rcti *rect, const float color[4])
2793 {
2794         float coord_array[LINK_RESOL + 1][2];
2795
2796         if (ui_link_bezier_points(rect, coord_array, LINK_RESOL)) {
2797                 unsigned int pos = add_attrib(immVertexFormat(), "pos", GL_FLOAT, 2, KEEP_FLOAT);
2798                 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2799
2800 #if 0 /* unused */
2801                 /* we can reuse the dist variable here to increment the GL curve eval amount*/
2802                 const float dist = 1.0f / (float)LINK_RESOL;
2803 #endif
2804                 glEnable(GL_BLEND);
2805                 glEnable(GL_LINE_SMOOTH);
2806
2807                 immUniformColor4fv(color);
2808
2809                 immBegin(GL_LINE_STRIP, LINK_RESOL + 1);
2810                 for (int i = 0; i <= LINK_RESOL; ++i)
2811                         immVertex2fv(pos, coord_array[i]);
2812                 immEnd();
2813
2814                 glDisable(GL_BLEND);
2815                 glDisable(GL_LINE_SMOOTH);
2816
2817                 immUnbindProgram();
2818         }
2819 }
2820
2821 /* function in use for buttons and for view2d sliders */
2822 void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *slider, int state)
2823 {
2824         uiWidgetBase wtb;
2825         int horizontal;
2826         float rad;
2827         bool outline = false;
2828
2829         widget_init(&wtb);
2830
2831         /* determine horizontal/vertical */
2832         horizontal = (BLI_rcti_size_x(rect) > BLI_rcti_size_y(rect));
2833
2834         if (horizontal)
2835                 rad = 0.5f * BLI_rcti_size_y(rect);
2836         else
2837                 rad = 0.5f * BLI_rcti_size_x(rect);
2838         
2839         wtb.draw_shadedir = (horizontal) ? true : false;
2840         
2841         /* draw back part, colors swapped and shading inverted */
2842         if (horizontal)
2843                 SWAP(short, wcol->shadetop, wcol->shadedown);
2844         
2845         round_box_edges(&wtb, UI_CNR_ALL, rect, rad);
2846         widgetbase_draw(&wtb, wcol);
2847         
2848         /* slider */
2849         if ((BLI_rcti_size_x(slider) < 2) || (BLI_rcti_size_y(slider) < 2)) {
2850                 /* pass */
2851         }
2852         else {
2853                 SWAP(short, wcol->shadetop, wcol->shadedown);
2854                 
2855                 copy_v4_v4_char(wcol->inner, wcol->item);
2856                 
2857                 if (wcol->shadetop > wcol->shadedown)
2858                         wcol->shadetop += 20;   /* XXX violates themes... */
2859                 else wcol->shadedown += 20;
2860                 
2861                 if (state & UI_SCROLL_PRESSED) {
2862                         wcol->inner[0] = wcol->inner[0] >= 250 ? 255 : wcol->inner[0] + 5;
2863                         wcol->inner[1] = wcol->inner[1] >= 250 ? 255 : wcol->inner[1] + 5;
2864                         wcol->inner[2] = wcol->inner[2] >= 250 ? 255 : wcol->inner[2] + 5;
2865                 }
2866
2867                 /* draw */
2868                 wtb.draw_emboss = false; /* only emboss once */
2869                 
2870                 /* exception for progress bar */
2871                 if (state & UI_SCROLL_NO_OUTLINE) {
2872                         SWAP(bool, outline, wtb.draw_outline);
2873                 }
2874                 
2875                 round_box_edges(&wtb, UI_CNR_ALL, slider, rad);
2876                 
2877                 if (state & UI_SCROLL_ARROWS) {
2878                         if (wcol->item[0] > 48) wcol->item[0] -= 48;
2879                         if (wcol->item[1] > 48) wcol->item[1] -= 48;
2880                         if (wcol->item[2] > 48) wcol->item[2] -= 48;
2881                         wcol->item[3] = 255;
2882                         
2883                         if (horizontal) {
2884                                 widget_scroll_circle(&wtb.tria1, slider, 0.6f, 'l');
2885                                 widget_scroll_circle(&wtb.tria2, slider, 0.6f, 'r');
2886                         }
2887                         else {
2888                                 widget_scroll_circle(&wtb.tria1, slider, 0.6f, 'b');
2889                                 widget_scroll_circle(&wtb.tria2, slider, 0.6f, 't');
2890                         }
2891                 }
2892                 widgetbase_draw(&wtb, wcol);
2893                 
2894                 if (state & UI_SCROLL_NO_OUTLINE) {
2895                         SWAP(bool, outline, wtb.draw_outline);
2896                 }
2897         }
2898 }
2899
2900 static void widget_scroll(uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int UNUSED(roundboxalign))
2901 {
2902         rcti rect1;
2903         double value;
2904         float fac, size, min;
2905         int horizontal;
2906
2907         /* calculate slider part */
2908         value = ui_but_value_get(but);
2909
2910         size = (but->softmax + but->a1 - but->softmin);
2911         size = max_ff(size, 2.0f);
2912         
2913         /* position */
2914         rect1 = *rect;
2915
2916         /* determine horizontal/vertical */
2917         horizontal = (BLI_rcti_size_x(rect) > BLI_rcti_size_y(rect));
2918         
2919         if (horizontal) {
2920                 fac = BLI_rcti_size_x(rect) / size;
2921                 rect1.xmin = rect1.xmin + ceilf(fac * ((float)value - but->softmin));
2922                 rect1.xmax = rect1.xmin + ceilf(fac * (but->a1 - but->softmin));
2923
2924                 /* ensure minimium size */
2925                 min = BLI_rcti_size_y(rect);
2926
2927                 if (BLI_rcti_size_x(&rect1) < min) {
2928                         rect1.xmax = rect1.xmin + min;
2929
2930                         if (rect1.xmax > rect->xmax) {
2931                                 rect1.xmax = rect->xmax;
2932                                 rect1.xmin = max_ii(rect1.xmax - min, rect->xmin);
2933                         }
2934                 }
2935         }
2936         else {
2937                 fac = BLI_rcti_size_y(rect) / size;
2938                 rect1.ymax = rect1.ymax - ceilf(fac * ((float)value - but->softmin));
2939                 rect1.ymin = rect1.ymax - ceilf(fac * (but->a1 - but->softmin));
2940
2941                 /* ensure minimium size */
2942                 min = BLI_rcti_size_x(rect);
2943
2944                 if (BLI_rcti_size_y(&rect1) < min) {
2945                         rect1.ymax = rect1.ymin + min;
2946
2947                         if (rect1.ymax > rect->ymax) {
2948                                 rect1.ymax = rect->ymax;
2949                                 rect1.ymin = max_ii(rect1.ymax - min, rect->ymin);
2950                         }
2951                 }
2952         }
2953
2954         if (state & UI_SELECT)
2955                 state = UI_SCROLL_PRESSED;
2956         else
2957                 state = 0;
2958         UI_draw_widget_scroll(wcol, rect, &rect1, state);
2959 }
2960
2961 static void widget_progressbar(uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign)
2962 {
2963         uiWidgetBase wtb, wtb_bar;
2964         rcti rect_prog = *rect, rect_bar = *rect;
2965
2966         widget_init(&wtb);
2967         widget_init(&wtb_bar);
2968
2969         /* round corners */
2970         float value = but->a1;
2971         float offs = 0.25f * BLI_rcti_size_y(&rect_prog);
2972         float w = value * BLI_rcti_size_x(&rect_prog);
2973
2974         /* ensure minimium size */
2975         w = MAX2(w, offs);
2976
2977         rect_bar.xmax = rect_bar.xmin + w;
2978
2979         round_box_edges(&wtb, roundboxalign, &rect_prog, offs);
2980         round_box_edges(&wtb_bar, roundboxalign, &rect_bar, offs);
2981
2982         wtb.draw_outline = true;
2983         widgetbase_draw(&wtb, wcol);
2984
2985         /* "slider" bar color */
2986         copy_v3_v3_char(wcol->inner, wcol->item);
2987         widgetbase_draw(&wtb_bar, wcol);
2988
2989         /* raise text a bit */
2990         rect->xmin += (BLI_rcti_size_x(&rect_prog) / 2);
2991         rect->xmax += (BLI_rcti_size_x(&rect_prog) / 2);
2992 }
2993
2994 static void widget_link(uiBut *but, uiWidgetColors *UNUSED(wcol), rcti *rect, int UNUSED(state), int UNUSED(roundboxalign))
2995 {
2996         
2997         if (but->flag & UI_SELECT) {
2998                 rcti rectlink;
2999                 float color[4];
3000                 
3001                 UI_GetThemeColor4fv(TH_TEXT_HI, color);
3002                 
3003                 rectlink.xmin = BLI_rcti_cent_x(rect);
3004                 rectlink.ymin = BLI_rcti_cent_y(rect);
3005                 rectlink.xmax = but->linkto[0];
3006                 rectlink.ymax = but->linkto[1];
3007                 
3008                 ui_draw_link_bezier(&rectlink, color);
3009         }
3010 }
3011
3012 static void widget_numslider(uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
3013 {
3014         uiWidgetBase wtb, wtb1;
3015         rcti rect1;
3016         double value;
3017         float offs, toffs, fac = 0;
3018         char outline[3];
3019
3020         widget_init(&wtb);
3021         widget_init(&wtb1);
3022         
3023         /* backdrop first */
3024         
3025         /* fully rounded */
3026         offs = 0.5f * BLI_rcti_size_y(rect);
3027         toffs = offs * 0.75f;
3028         round_box_edges(&wtb, roundboxalign, rect, offs);
3029
3030         wtb.draw_outline = false;
3031         widgetbase_draw(&wtb, wcol);
3032         
3033         /* draw left/right parts only when not in text editing */
3034         if (!(state & UI_TEXTINPUT)) {
3035                 int roundboxalign_slider;
3036                 
3037                 /* slider part */
3038                 copy_v3_v3_char(outline, wcol->outline);
3039                 copy_v3_v3_char(wcol->outline, wcol->item);
3040                 copy_v3_v3_char(wcol->inner, wcol->item);
3041
3042                 if (!(state & UI_SELECT))
3043                         SWAP(short, wcol->shadetop, wcol->shadedown);
3044                 
3045                 rect1 = *rect;
3046                 
3047                 value = ui_but_value_get(but);
3048                 if ((but->softmax - but->softmin) > 0) {
3049                         fac = ((float)value - but->softmin) * (BLI_rcti_size_x(&rect1) - offs) / (but->softmax - but->softmin);
3050                 }
3051                 
3052                 /* left part of slider, always rounded */
3053                 rect1.xmax = rect1.xmin + ceil(offs + U.pixelsize);
3054                 round_box_edges(&wtb1, roundboxalign & ~(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT), &rect1, offs);
3055                 wtb1.draw_outline = false;
3056                 widgetbase_draw(&wtb1, wcol);
3057                 
3058                 /* right part of slider, interpolate roundness */
3059                 rect1.xmax = rect1.xmin + fac + offs;
3060                 rect1.xmin +=  floor(offs - U.pixelsize);
3061                 
3062                 if (rect1.xmax + offs > rect->xmax) {
3063                         roundboxalign_slider = roundboxalign & ~(UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT);
3064                         offs *= (rect1.xmax + offs - rect->xmax) / offs;
3065                 }
3066                 else {
3067                         roundboxalign_slider = 0;
3068                         offs = 0.0f;
3069                 }
3070                 round_box_edges(&wtb1, roundboxalign_slider, &rect1, offs);
3071                 
3072                 widgetbase_draw(&wtb1, wcol);
3073                 copy_v3_v3_char(wcol->outline, outline);
3074                 
3075                 if (!(state & UI_SELECT))
3076                         SWAP(short, wcol->shadetop, wcol->shadedown);
3077         }
3078         
3079         /* outline */
3080         wtb.draw_outline = true;
3081         wtb.draw_inner = false;
3082         widgetbase_draw(&wtb, wcol);
3083
3084         /* add space at either side of the button so text aligns with numbuttons (which have arrow icons) */
3085         if (!(state & UI_TEXTINPUT)) {
3086                 rect->xmax -= toffs;
3087                 rect->xmin += toffs;
3088         }
3089 }
3090
3091 /* I think 3 is sufficient border to indicate keyed status */
3092 #define SWATCH_KEYED_BORDER 3
3093
3094 static void widget_swatch(uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
3095 {
3096         uiWidgetBase wtb;
3097         float rad, col[4];
3098         bool color_profile = but->block->color_profile;
3099         
3100         col[3] = 1.0f;
3101
3102         if (but->rnaprop) {
3103                 BLI_assert(but->rnaindex == -1);
3104
3105                 if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA)
3106                         color_profile = false;
3107
3108                 if (RNA_property_array_length(&but->rnapoin, but->rnaprop) == 4) {
3109                         col[3] = RNA_property_float_get_index(&but->rnapoin, but->rnaprop, 3);
3110                 }
3111         }
3112         
3113         widget_init(&wtb);
3114         
3115         /* half rounded */
3116         rad = 0.25f * U.widget_unit;
3117         round_box_edges(&wtb, roundboxalign, rect, rad);
3118                 
3119         ui_but_v3_get(but, col);
3120
3121         if (state & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_REDALERT)) {
3122                 /* draw based on state - color for keyed etc */
3123                 widgetbase_draw(&wtb, wcol);
3124
3125                 /* inset to draw swatch color */
3126                 rect->xmin += SWATCH_KEYED_BORDER;
3127                 rect->xmax -= SWATCH_KEYED_BORDER;
3128                 rect->ymin += SWATCH_KEYED_BORDER;
3129                 rect->ymax -= SWATCH_KEYED_BORDER;
3130                 
3131                 round_box_edges(&wtb, roundboxalign, rect, rad);
3132         }
3133         
3134         if (color_profile)
3135                 ui_block_cm_to_display_space_v3(but->block, col);
3136         
3137         rgba_float_to_uchar((unsigned char *)wcol->inner,