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