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