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