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