Cleanp: fix compiler warnings
[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   UI_WTYPE_NODESOCKET,
110 } uiWidgetTypeEnum;
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 = (UI_STATE_HOLD_ACTION | UI_STATE_TEXT_INPUT | UI_STATE_ACTIVE_LEFT |
123                         UI_STATE_ACTIVE_RIGHT | UI_STATE_TEXT_BEFORE_WIDGET),
124 };
125 /* Prevent accidental use. */
126 #define UI_BUT_UPDATE_DELAY ((void)0)
127 #define UI_BUT_UNDO ((void)0)
128
129 /* ************** widget base functions ************** */
130 /**
131  * - in: roundbox codes for corner types and radius
132  * - return: array of `[size][2][x, y]` points, the edges of the roundbox, + UV coords
133  *
134  * - draw black box with alpha 0 on exact button boundbox
135  * - for every AA step:
136  *    - draw the inner part for a round filled box, with color blend codes or texture coords
137  *    - draw outline in outline color
138  *    - draw outer part, bottom half, extruded 1 pixel to bottom, for emboss shadow
139  *    - draw extra decorations
140  * - draw background color box with alpha 1 on exact button boundbox
141  */
142
143 /* fill this struct with polygon info to draw AA'ed */
144 /* it has outline, back, and two optional tria meshes */
145
146 typedef struct uiWidgetTrias {
147   uint tot;
148   int type;
149   float size, center[2];
150
151   float vec[16][2];
152   const uint (*index)[3];
153
154 } uiWidgetTrias;
155
156 /* max as used by round_box__edges */
157 /* Make sure to change widget_base_vert.glsl accordingly. */
158 #define WIDGET_CURVE_RESOLU 9
159 #define WIDGET_SIZE_MAX (WIDGET_CURVE_RESOLU * 4)
160
161 typedef struct uiWidgetBase {
162   /* TODO remove these completely */
163   int totvert, halfwayvert;
164   float outer_v[WIDGET_SIZE_MAX][2];
165   float inner_v[WIDGET_SIZE_MAX][2];
166   float inner_uv[WIDGET_SIZE_MAX][2];
167
168   bool draw_inner, draw_outline, draw_emboss;
169
170   uiWidgetTrias tria1;
171   uiWidgetTrias tria2;
172
173   /* Widget shader parameters, must match the shader layout. */
174   uiWidgetBaseParameters uniform_params;
175 } uiWidgetBase;
176
177 /** uiWidgetType: for time being only for visual appearance,
178  * later, a handling callback can be added too
179  */
180 typedef struct uiWidgetType {
181
182   /* pointer to theme color definition */
183   const uiWidgetColors *wcol_theme;
184   uiWidgetStateColors *wcol_state;
185
186   /* converted colors for state */
187   uiWidgetColors wcol;
188
189   void (*state)(struct uiWidgetType *, int state, int drawflag);
190   void (*draw)(uiWidgetColors *, rcti *, int state, int roundboxalign);
191   void (*custom)(uiBut *, uiWidgetColors *, rcti *, int state, int roundboxalign);
192   void (*text)(const uiFontStyle *, const uiWidgetColors *, uiBut *, rcti *);
193
194 } uiWidgetType;
195
196 /* *********************** draw data ************************** */
197
198 static const float cornervec[WIDGET_CURVE_RESOLU][2] = {
199     {0.0, 0.0},
200     {0.195, 0.02},
201     {0.383, 0.067},
202     {0.55, 0.169},
203     {0.707, 0.293},
204     {0.831, 0.45},
205     {0.924, 0.617},
206     {0.98, 0.805},
207     {1.0, 1.0},
208 };
209
210 const float ui_pixel_jitter[UI_PIXEL_AA_JITTER][2] = {
211     {0.468813, -0.481430},
212     {-0.155755, -0.352820},
213     {0.219306, -0.238501},
214     {-0.393286, -0.110949},
215     {-0.024699, 0.013908},
216     {0.343805, 0.147431},
217     {-0.272855, 0.269918},
218     {0.095909, 0.388710},
219 };
220 #define WIDGET_AA_JITTER UI_PIXEL_AA_JITTER
221 #define jit ui_pixel_jitter
222
223 /* -------------------------------------------------------------------- */
224 /** \name Shape Preset Data
225  * \{ */
226
227 static const float g_shape_preset_number_arrow_vert[3][2] = {
228     {-0.352077, 0.532607},
229     {-0.352077, -0.549313},
230     {0.330000, -0.008353},
231 };
232 static const uint g_shape_preset_number_arrow_face[1][3] = {
233     {0, 1, 2},
234 };
235
236 static const float g_shape_preset_scroll_circle_vert[16][2] = {
237     {0.382684, 0.923879},
238     {0.000001, 1.000000},
239     {-0.382683, 0.923880},
240     {-0.707107, 0.707107},
241     {-0.923879, 0.382684},
242     {-1.000000, 0.000000},
243     {-0.923880, -0.382684},
244     {-0.707107, -0.707107},
245     {-0.382683, -0.923880},
246     {0.000000, -1.000000},
247     {0.382684, -0.923880},
248     {0.707107, -0.707107},
249     {0.923880, -0.382684},
250     {1.000000, -0.000000},
251     {0.923880, 0.382683},
252     {0.707107, 0.707107},
253 };
254 static const uint g_shape_preset_scroll_circle_face[14][3] = {
255     {0, 1, 2},
256     {2, 0, 3},
257     {3, 0, 15},
258     {3, 15, 4},
259     {4, 15, 14},
260     {4, 14, 5},
261     {5, 14, 13},
262     {5, 13, 6},
263     {6, 13, 12},
264     {6, 12, 7},
265     {7, 12, 11},
266     {7, 11, 8},
267     {8, 11, 10},
268     {8, 10, 9},
269 };
270
271 static const float g_shape_preset_menu_arrow_vert[6][2] = {
272     {-0.33, 0.16},
273     {0.33, 0.16},
274     {0, 0.82},
275     {0, -0.82},
276     {-0.33, -0.16},
277     {0.33, -0.16},
278 };
279 static const uint g_shape_preset_menu_arrow_face[2][3] = {{2, 0, 1}, {3, 5, 4}};
280
281 static const float g_shape_preset_checkmark_vert[6][2] = {
282     {-0.578579, 0.253369},
283     {-0.392773, 0.412794},
284     {-0.004241, -0.328551},
285     {-0.003001, 0.034320},
286     {1.055313, 0.864744},
287     {0.866408, 1.026895},
288 };
289
290 static const uint g_shape_preset_checkmark_face[4][3] = {
291     {3, 2, 4},
292     {3, 4, 5},
293     {1, 0, 3},
294     {0, 2, 3},
295 };
296
297 #define OY (-0.2 / 2)
298 #define SC (0.35 * 2)
299 static const float g_shape_preset_hold_action_vert[6][2] = {
300     {-0.5 + SC, 1.0 + OY},
301     {0.5, 1.0 + OY},
302     {0.5, 0.0 + OY + SC},
303 };
304 static const uint g_shape_preset_hold_action_face[2][3] = {{2, 0, 1}, {3, 5, 4}};
305 #undef OY
306 #undef SC
307
308 /** \} */
309
310 /* **************** Batch creations ****************** */
311 /**
312  * In order to speed up UI drawing we create some batches that are then
313  * modified by specialized shaders to draw certain elements really fast.
314  * TODO: find a better place. Maybe it's own file?
315  */
316
317 /* offset in triavec[] in shader per type */
318 static const int tria_ofs[ROUNDBOX_TRIA_MAX] = {
319     [ROUNDBOX_TRIA_NONE] = 0,
320     [ROUNDBOX_TRIA_ARROWS] = 0,
321     [ROUNDBOX_TRIA_SCROLL] = 12,
322     [ROUNDBOX_TRIA_MENU] = 28,
323     [ROUNDBOX_TRIA_CHECK] = 34,
324     [ROUNDBOX_TRIA_HOLD_ACTION_ARROW] = 40,
325 };
326 static const int tria_vcount[ROUNDBOX_TRIA_MAX] = {
327     [ROUNDBOX_TRIA_NONE] = 0,
328     [ROUNDBOX_TRIA_ARROWS] = 6,
329     [ROUNDBOX_TRIA_SCROLL] = 16,
330     [ROUNDBOX_TRIA_MENU] = 6,
331     [ROUNDBOX_TRIA_CHECK] = 6,
332     [ROUNDBOX_TRIA_HOLD_ACTION_ARROW] = 3,
333 };
334
335 static struct {
336   GPUBatch *roundbox_widget[ROUNDBOX_TRIA_MAX];
337
338   GPUBatch *roundbox_simple;
339   GPUBatch *roundbox_simple_aa;
340   GPUBatch *roundbox_simple_outline;
341   GPUBatch *roundbox_shadow;
342
343   GPUVertFormat format;
344   uint vflag_id;
345 } g_ui_batch_cache = {{0}};
346
347 static GPUVertFormat *vflag_format(void)
348 {
349   if (g_ui_batch_cache.format.attr_len == 0) {
350     GPUVertFormat *format = &g_ui_batch_cache.format;
351     g_ui_batch_cache.vflag_id = GPU_vertformat_attr_add(
352         format, "vflag", GPU_COMP_U32, 1, GPU_FETCH_INT);
353   }
354   return &g_ui_batch_cache.format;
355 }
356
357 #define INNER 0
358 #define OUTLINE 1
359 #define EMBOSS 2
360 #define NO_AA WIDGET_AA_JITTER
361
362 static void set_roundbox_vertex_data(GPUVertBufRaw *vflag_step, uint32_t d)
363 {
364   uint32_t *data = GPU_vertbuf_raw_step(vflag_step);
365   *data = d;
366 }
367
368 static uint32_t set_roundbox_vertex(GPUVertBufRaw *vflag_step,
369                                     int corner_id,
370                                     int corner_v,
371                                     int jit_v,
372                                     bool inner,
373                                     bool emboss,
374                                     int color)
375 {
376   uint32_t *data = GPU_vertbuf_raw_step(vflag_step);
377   *data = corner_id;
378   *data |= corner_v << 2;
379   *data |= jit_v << 6;
380   *data |= color << 12;
381   *data |= (inner) ? (1 << 10) : 0;  /* is inner vert */
382   *data |= (emboss) ? (1 << 11) : 0; /* is emboss vert */
383   return *data;
384 }
385
386 static uint32_t set_tria_vertex(
387     GPUVertBufRaw *vflag_step, int tria_type, int tria_v, int tria_id, int jit_v)
388 {
389   uint32_t *data = GPU_vertbuf_raw_step(vflag_step);
390   if (ELEM(tria_type, ROUNDBOX_TRIA_ARROWS)) {
391     tria_v += tria_id * tria_vcount[ROUNDBOX_TRIA_ARROWS];
392   }
393   *data = tria_ofs[tria_type] + tria_v;
394   *data |= jit_v << 6;
395   *data |= (tria_id == 0) ? (1 << 10) : 0; /* is first tria */
396   *data |= 1 << 14;                        /* is tria vert */
397   return *data;
398 }
399
400 static void roundbox_batch_add_tria(GPUVertBufRaw *vflag_step, int tria, uint32_t last_data)
401 {
402   const int tria_num =
403       ELEM(tria, ROUNDBOX_TRIA_CHECK, ROUNDBOX_TRIA_HOLD_ACTION_ARROW, ROUNDBOX_TRIA_MENU) ? 1 : 2;
404   /* for each tria */
405   for (int t = 0; t < tria_num; ++t) {
406     for (int j = 0; j < WIDGET_AA_JITTER; j++) {
407       /* restart */
408       set_roundbox_vertex_data(vflag_step, last_data);
409       set_tria_vertex(vflag_step, tria, 0, t, j);
410       for (int v = 0; v < tria_vcount[tria]; v++) {
411         last_data = set_tria_vertex(vflag_step, tria, v, t, j);
412       }
413     }
414   }
415 }
416
417 GPUBatch *ui_batch_roundbox_widget_get(int tria)
418 {
419   if (g_ui_batch_cache.roundbox_widget[tria] == NULL) {
420     uint32_t last_data;
421     GPUVertBufRaw vflag_step;
422     GPUVertBuf *vbo = GPU_vertbuf_create_with_format(vflag_format());
423     int vcount = WIDGET_SIZE_MAX;                                 /* inner */
424     vcount += 2;                                                  /* restart */
425     vcount += ((WIDGET_SIZE_MAX + 1) * 2) * WIDGET_AA_JITTER;     /* outline (edges) */
426     vcount += 2;                                                  /* restart */
427     vcount += ((WIDGET_CURVE_RESOLU * 2) * 2) * WIDGET_AA_JITTER; /* emboss */
428     if (tria) {
429       vcount += (tria_vcount[tria] + 2) * WIDGET_AA_JITTER; /* tria1 */
430       if (!ELEM(tria, ROUNDBOX_TRIA_CHECK, ROUNDBOX_TRIA_HOLD_ACTION_ARROW, ROUNDBOX_TRIA_MENU)) {
431         vcount += (tria_vcount[tria] + 2) * WIDGET_AA_JITTER; /* tria2 */
432       }
433     }
434     GPU_vertbuf_data_alloc(vbo, vcount);
435     GPU_vertbuf_attr_get_raw_data(vbo, g_ui_batch_cache.vflag_id, &vflag_step);
436     /* Inner */
437     for (int c1 = 0, c2 = 3; c1 < 2; c1++, c2--) {
438       for (int a1 = 0, a2 = WIDGET_CURVE_RESOLU - 1; a2 >= 0; a1++, a2--) {
439         last_data = set_roundbox_vertex(&vflag_step, c1, a1, NO_AA, true, false, INNER);
440         last_data = set_roundbox_vertex(&vflag_step, c2, a2, NO_AA, true, false, INNER);
441       }
442     }
443     /* restart */
444     set_roundbox_vertex_data(&vflag_step, last_data);
445     set_roundbox_vertex(&vflag_step, 0, 0, 0, true, false, OUTLINE);
446     /* Outlines */
447     for (int j = 0; j < WIDGET_AA_JITTER; j++) {
448       for (int c = 0; c < 4; c++) {
449         for (int a = 0; a < WIDGET_CURVE_RESOLU; a++) {
450           set_roundbox_vertex(&vflag_step, c, a, j, true, false, OUTLINE);
451           set_roundbox_vertex(&vflag_step, c, a, j, false, false, OUTLINE);
452         }
453       }
454       /* Close the loop. */
455       set_roundbox_vertex(&vflag_step, 0, 0, j, true, false, OUTLINE);
456       last_data = set_roundbox_vertex(&vflag_step, 0, 0, j, false, false, OUTLINE);
457     }
458     /* restart */
459     set_roundbox_vertex_data(&vflag_step, last_data);
460     set_roundbox_vertex(&vflag_step, 0, 0, 0, false, false, EMBOSS);
461     /* Emboss */
462     /* go back and forth : avoid degenerate triangle (but beware of backface cull) */
463     bool rev = false;
464     for (int j = 0; j < WIDGET_AA_JITTER; j++, rev = !rev) {
465       for (int c = (rev) ? 1 : 0; (rev) ? c >= 0 : c < 2; (rev) ? c-- : c++) {
466         int sta = (rev) ? WIDGET_CURVE_RESOLU - 1 : 0;
467         int end = WIDGET_CURVE_RESOLU;
468         for (int a = sta; (rev) ? a >= 0 : a < end; (rev) ? a-- : a++) {
469           set_roundbox_vertex(&vflag_step, c, a, j, false, false, EMBOSS);
470           last_data = set_roundbox_vertex(&vflag_step, c, a, j, false, true, EMBOSS);
471         }
472       }
473     }
474     if (tria) {
475       roundbox_batch_add_tria(&vflag_step, tria, last_data);
476     }
477     g_ui_batch_cache.roundbox_widget[tria] = GPU_batch_create_ex(
478         GPU_PRIM_TRI_STRIP, vbo, NULL, GPU_BATCH_OWNS_VBO);
479     gpu_batch_presets_register(g_ui_batch_cache.roundbox_widget[tria]);
480   }
481   return g_ui_batch_cache.roundbox_widget[tria];
482 }
483
484 GPUBatch *ui_batch_roundbox_get(bool filled, bool antialiased)
485 {
486   GPUBatch **batch = NULL;
487
488   if (filled) {
489     if (antialiased) {
490       batch = &g_ui_batch_cache.roundbox_simple_aa;
491     }
492     else {
493       batch = &g_ui_batch_cache.roundbox_simple;
494     }
495   }
496   else {
497     if (antialiased) {
498       BLI_assert(0); /* Use GL_LINE_SMOOTH instead!!: */
499     }
500     else {
501       batch = &g_ui_batch_cache.roundbox_simple_outline;
502     }
503   }
504
505   if (*batch == NULL) {
506     uint32_t last_data;
507     GPUVertBufRaw vflag_step;
508     GPUVertBuf *vbo = GPU_vertbuf_create_with_format(vflag_format());
509     int vcount = WIDGET_SIZE_MAX;
510     vcount += (filled) ? 2 : 0;
511     vcount *= (antialiased) ? WIDGET_AA_JITTER : 1;
512     GPU_vertbuf_data_alloc(vbo, vcount);
513     GPU_vertbuf_attr_get_raw_data(vbo, g_ui_batch_cache.vflag_id, &vflag_step);
514
515     if (filled) {
516       for (int j = 0; j < WIDGET_AA_JITTER; j++) {
517         if (!antialiased) {
518           j = NO_AA;
519         }
520         /* restart */
521         set_roundbox_vertex(&vflag_step, 0, 0, j, true, false, INNER);
522         for (int c1 = 0, c2 = 3; c1 < 2; c1++, c2--) {
523           for (int a1 = 0, a2 = WIDGET_CURVE_RESOLU - 1; a2 >= 0; a1++, a2--) {
524             last_data = set_roundbox_vertex(&vflag_step, c1, a1, j, true, false, INNER);
525             last_data = set_roundbox_vertex(&vflag_step, c2, a2, j, true, false, INNER);
526           }
527         }
528         /* restart */
529         set_roundbox_vertex_data(&vflag_step, last_data);
530         if (!antialiased) {
531           break;
532         }
533       }
534       *batch = GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, NULL, GPU_BATCH_OWNS_VBO);
535     }
536     else {
537       for (int j = 0; j < WIDGET_AA_JITTER; j++) {
538         if (!antialiased) {
539           j = NO_AA;
540         }
541         for (int c = 0; c < 4; c++) {
542           for (int a = 0; a < WIDGET_CURVE_RESOLU; a++) {
543             set_roundbox_vertex(&vflag_step, c, a, j, true, false, INNER);
544           }
545         }
546         if (!antialiased) {
547           break;
548         }
549       }
550       *batch = GPU_batch_create_ex(GPU_PRIM_LINE_LOOP, vbo, NULL, GPU_BATCH_OWNS_VBO);
551     }
552
553     gpu_batch_presets_register(*batch);
554   }
555   return *batch;
556 }
557
558 GPUBatch *ui_batch_roundbox_shadow_get(void)
559 {
560   if (g_ui_batch_cache.roundbox_shadow == NULL) {
561     uint32_t last_data;
562     GPUVertBufRaw vflag_step;
563     GPUVertBuf *vbo = GPU_vertbuf_create_with_format(vflag_format());
564     int vcount = (WIDGET_SIZE_MAX + 1) * 2 + 2 + WIDGET_SIZE_MAX;
565     GPU_vertbuf_data_alloc(vbo, vcount);
566     GPU_vertbuf_attr_get_raw_data(vbo, g_ui_batch_cache.vflag_id, &vflag_step);
567
568     for (int c = 0; c < 4; c++) {
569       for (int a = 0; a < WIDGET_CURVE_RESOLU; a++) {
570         set_roundbox_vertex(&vflag_step, c, a, NO_AA, true, false, INNER);
571         set_roundbox_vertex(&vflag_step, c, a, NO_AA, false, false, INNER);
572       }
573     }
574     /* close loop */
575     last_data = set_roundbox_vertex(&vflag_step, 0, 0, NO_AA, true, false, INNER);
576     last_data = set_roundbox_vertex(&vflag_step, 0, 0, NO_AA, false, false, INNER);
577     /* restart */
578     set_roundbox_vertex_data(&vflag_step, last_data);
579     set_roundbox_vertex(&vflag_step, 0, 0, NO_AA, true, false, INNER);
580     /* filled */
581     for (int c1 = 0, c2 = 3; c1 < 2; c1++, c2--) {
582       for (int a1 = 0, a2 = WIDGET_CURVE_RESOLU - 1; a2 >= 0; a1++, a2--) {
583         set_roundbox_vertex(&vflag_step, c1, a1, NO_AA, true, false, INNER);
584         set_roundbox_vertex(&vflag_step, c2, a2, NO_AA, true, false, INNER);
585       }
586     }
587     g_ui_batch_cache.roundbox_shadow = GPU_batch_create_ex(
588         GPU_PRIM_TRI_STRIP, vbo, NULL, GPU_BATCH_OWNS_VBO);
589     gpu_batch_presets_register(g_ui_batch_cache.roundbox_shadow);
590   }
591   return g_ui_batch_cache.roundbox_shadow;
592 }
593
594 #undef INNER
595 #undef OUTLINE
596 #undef EMBOSS
597 #undef NO_AA
598
599 /* ************************************************* */
600
601 void UI_draw_anti_tria(
602     float x1, float y1, float x2, float y2, float x3, float y3, const float color[4])
603 {
604   float tri_arr[3][2] = {{x1, y1}, {x2, y2}, {x3, y3}};
605   float draw_color[4];
606
607   copy_v4_v4(draw_color, color);
608   /* Note: This won't give back the original color. */
609   draw_color[3] *= 1.0f / WIDGET_AA_JITTER;
610
611   GPU_blend(true);
612
613   uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
614   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
615
616   immUniformColor4fv(draw_color);
617   immBegin(GPU_PRIM_TRIS, 3 * WIDGET_AA_JITTER);
618
619   /* for each AA step */
620   for (int j = 0; j < WIDGET_AA_JITTER; j++) {
621     immVertex2f(pos, tri_arr[0][0] + jit[j][0], tri_arr[0][1] + jit[j][1]);
622     immVertex2f(pos, tri_arr[1][0] + jit[j][0], tri_arr[1][1] + jit[j][1]);
623     immVertex2f(pos, tri_arr[2][0] + jit[j][0], tri_arr[2][1] + jit[j][1]);
624   }
625
626   immEnd();
627
628   immUnbindProgram();
629
630   GPU_blend(false);
631 }
632
633 /* triangle 'icon' inside rect */
634 void ui_draw_anti_tria_rect(const rctf *rect, char dir, const float color[4])
635 {
636   if (dir == 'h') {
637     float half = 0.5f * BLI_rctf_size_y(rect);
638     UI_draw_anti_tria(
639         rect->xmin, rect->ymin, rect->xmin, rect->ymax, rect->xmax, rect->ymin + half, color);
640   }
641   else {
642     float half = 0.5f * BLI_rctf_size_x(rect);
643     UI_draw_anti_tria(
644         rect->xmin, rect->ymax, rect->xmax, rect->ymax, rect->xmin + half, rect->ymin, color);
645   }
646 }
647
648 void UI_draw_anti_fan(float tri_array[][2], uint length, const float color[4])
649 {
650   float draw_color[4];
651
652   copy_v4_v4(draw_color, color);
653   draw_color[3] *= 2.0f / WIDGET_AA_JITTER;
654
655   GPU_blend(true);
656
657   uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
658   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
659
660   immUniformColor4fv(draw_color);
661
662   /* for each AA step */
663   for (int j = 0; j < WIDGET_AA_JITTER; j++) {
664     immBegin(GPU_PRIM_TRI_FAN, length);
665     immVertex2f(pos, tri_array[0][0], tri_array[0][1]);
666     immVertex2f(pos, tri_array[1][0], tri_array[1][1]);
667
668     /* We jitter only the middle of the fan, the extremes are pinned. */
669     for (int i = 2; i < length - 1; i++) {
670       immVertex2f(pos, tri_array[i][0] + jit[j][0], tri_array[i][1] + jit[j][1]);
671     }
672
673     immVertex2f(pos, tri_array[length - 1][0], tri_array[length - 1][1]);
674     immEnd();
675   }
676
677   immUnbindProgram();
678
679   GPU_blend(false);
680 }
681
682 static void widget_init(uiWidgetBase *wtb)
683 {
684   wtb->totvert = wtb->halfwayvert = 0;
685   wtb->tria1.tot = 0;
686   wtb->tria2.tot = 0;
687   wtb->tria1.type = ROUNDBOX_TRIA_NONE;
688   wtb->tria1.size = 0;
689   wtb->tria2.size = 0;
690
691   wtb->draw_inner = true;
692   wtb->draw_outline = true;
693   wtb->draw_emboss = true;
694
695   wtb->uniform_params.shade_dir = 1.0f;
696   wtb->uniform_params.alpha_discard = 1.0f;
697 }
698
699 /* helper call, makes shadow rect, with 'sun' above menu, so only shadow to left/right/bottom */
700 /* return tot */
701 static int round_box_shadow_edges(
702     float (*vert)[2], const rcti *rect, float rad, int roundboxalign, float step)
703 {
704   float vec[WIDGET_CURVE_RESOLU][2];
705   float minx, miny, maxx, maxy;
706   int a, tot = 0;
707
708   rad += step;
709
710   if (2.0f * rad > BLI_rcti_size_y(rect)) {
711     rad = 0.5f * BLI_rcti_size_y(rect);
712   }
713
714   minx = rect->xmin - step;
715   miny = rect->ymin - step;
716   maxx = rect->xmax + step;
717   maxy = rect->ymax + step;
718
719   /* mult */
720   for (a = 0; a < WIDGET_CURVE_RESOLU; a++) {
721     vec[a][0] = rad * cornervec[a][0];
722     vec[a][1] = rad * cornervec[a][1];
723   }
724
725   /* start with left-top, anti clockwise */
726   if (roundboxalign & UI_CNR_TOP_LEFT) {
727     for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
728       vert[tot][0] = minx + rad - vec[a][0];
729       vert[tot][1] = maxy - vec[a][1];
730     }
731   }
732   else {
733     for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
734       vert[tot][0] = minx;
735       vert[tot][1] = maxy;
736     }
737   }
738
739   if (roundboxalign & UI_CNR_BOTTOM_LEFT) {
740     for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
741       vert[tot][0] = minx + vec[a][1];
742       vert[tot][1] = miny + rad - vec[a][0];
743     }
744   }
745   else {
746     for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
747       vert[tot][0] = minx;
748       vert[tot][1] = miny;
749     }
750   }
751
752   if (roundboxalign & UI_CNR_BOTTOM_RIGHT) {
753     for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
754       vert[tot][0] = maxx - rad + vec[a][0];
755       vert[tot][1] = miny + vec[a][1];
756     }
757   }
758   else {
759     for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
760       vert[tot][0] = maxx;
761       vert[tot][1] = miny;
762     }
763   }
764
765   if (roundboxalign & UI_CNR_TOP_RIGHT) {
766     for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
767       vert[tot][0] = maxx - vec[a][1];
768       vert[tot][1] = maxy - rad + vec[a][0];
769     }
770   }
771   else {
772     for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
773       vert[tot][0] = maxx;
774       vert[tot][1] = maxy;
775     }
776   }
777   return tot;
778 }
779
780 /* this call has 1 extra arg to allow mask outline */
781 static void round_box__edges(
782     uiWidgetBase *wt, int roundboxalign, const rcti *rect, float rad, float radi)
783 {
784   float vec[WIDGET_CURVE_RESOLU][2], veci[WIDGET_CURVE_RESOLU][2];
785   float minx = rect->xmin, miny = rect->ymin, maxx = rect->xmax, maxy = rect->ymax;
786   float minxi = minx + U.pixelsize; /* boundbox inner */
787   float maxxi = maxx - U.pixelsize;
788   float minyi = miny + U.pixelsize;
789   float maxyi = maxy - U.pixelsize;
790   /* for uv, can divide by zero */
791   float facxi = (maxxi != minxi) ? 1.0f / (maxxi - minxi) : 0.0f;
792   float facyi = (maxyi != minyi) ? 1.0f / (maxyi - minyi) : 0.0f;
793   int a, tot = 0, minsize;
794   const int hnum = ((roundboxalign & (UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT)) ==
795                         (UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT) ||
796                     (roundboxalign & (UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT)) ==
797                         (UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT)) ?
798                        1 :
799                        2;
800   const int vnum = ((roundboxalign & (UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT)) ==
801                         (UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT) ||
802                     (roundboxalign & (UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT)) ==
803                         (UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT)) ?
804                        1 :
805                        2;
806
807   minsize = min_ii(BLI_rcti_size_x(rect) * hnum, BLI_rcti_size_y(rect) * vnum);
808
809   if (2.0f * rad > minsize) {
810     rad = 0.5f * minsize;
811   }
812
813   if (2.0f * (radi + 1.0f) > minsize) {
814     radi = 0.5f * minsize - U.pixelsize;
815   }
816
817   wt->uniform_params.rad = rad;
818   wt->uniform_params.radi = radi;
819   wt->uniform_params.facxi = facxi;
820   wt->uniform_params.facyi = facyi;
821   wt->uniform_params.round_corners[0] = (roundboxalign & UI_CNR_BOTTOM_LEFT) ? 1.0f : 0.0f;
822   wt->uniform_params.round_corners[1] = (roundboxalign & UI_CNR_BOTTOM_RIGHT) ? 1.0f : 0.0f;
823   wt->uniform_params.round_corners[2] = (roundboxalign & UI_CNR_TOP_RIGHT) ? 1.0f : 0.0f;
824   wt->uniform_params.round_corners[3] = (roundboxalign & UI_CNR_TOP_LEFT) ? 1.0f : 0.0f;
825   BLI_rctf_rcti_copy(&wt->uniform_params.rect, rect);
826   BLI_rctf_init(&wt->uniform_params.recti, minxi, maxxi, minyi, maxyi);
827
828   /* mult */
829   for (a = 0; a < WIDGET_CURVE_RESOLU; a++) {
830     veci[a][0] = radi * cornervec[a][0];
831     veci[a][1] = radi * cornervec[a][1];
832     vec[a][0] = rad * cornervec[a][0];
833     vec[a][1] = rad * cornervec[a][1];
834   }
835
836   /* corner left-bottom */
837   if (roundboxalign & UI_CNR_BOTTOM_LEFT) {
838
839     for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
840       wt->inner_v[tot][0] = minxi + veci[a][1];
841       wt->inner_v[tot][1] = minyi + radi - veci[a][0];
842
843       wt->outer_v[tot][0] = minx + vec[a][1];
844       wt->outer_v[tot][1] = miny + rad - vec[a][0];
845
846       wt->inner_uv[tot][0] = facxi * (wt->inner_v[tot][0] - minxi);
847       wt->inner_uv[tot][1] = facyi * (wt->inner_v[tot][1] - minyi);
848     }
849   }
850   else {
851     wt->inner_v[tot][0] = minxi;
852     wt->inner_v[tot][1] = minyi;
853
854     wt->outer_v[tot][0] = minx;
855     wt->outer_v[tot][1] = miny;
856
857     wt->inner_uv[tot][0] = 0.0f;
858     wt->inner_uv[tot][1] = 0.0f;
859
860     tot++;
861   }
862
863   /* corner right-bottom */
864   if (roundboxalign & UI_CNR_BOTTOM_RIGHT) {
865
866     for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
867       wt->inner_v[tot][0] = maxxi - radi + veci[a][0];
868       wt->inner_v[tot][1] = minyi + veci[a][1];
869
870       wt->outer_v[tot][0] = maxx - rad + vec[a][0];
871       wt->outer_v[tot][1] = miny + vec[a][1];
872
873       wt->inner_uv[tot][0] = facxi * (wt->inner_v[tot][0] - minxi);
874       wt->inner_uv[tot][1] = facyi * (wt->inner_v[tot][1] - minyi);
875     }
876   }
877   else {
878     wt->inner_v[tot][0] = maxxi;
879     wt->inner_v[tot][1] = minyi;
880
881     wt->outer_v[tot][0] = maxx;
882     wt->outer_v[tot][1] = miny;
883
884     wt->inner_uv[tot][0] = 1.0f;
885     wt->inner_uv[tot][1] = 0.0f;
886
887     tot++;
888   }
889
890   wt->halfwayvert = tot;
891
892   /* corner right-top */
893   if (roundboxalign & UI_CNR_TOP_RIGHT) {
894
895     for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
896       wt->inner_v[tot][0] = maxxi - veci[a][1];
897       wt->inner_v[tot][1] = maxyi - radi + veci[a][0];
898
899       wt->outer_v[tot][0] = maxx - vec[a][1];
900       wt->outer_v[tot][1] = maxy - rad + vec[a][0];
901
902       wt->inner_uv[tot][0] = facxi * (wt->inner_v[tot][0] - minxi);
903       wt->inner_uv[tot][1] = facyi * (wt->inner_v[tot][1] - minyi);
904     }
905   }
906   else {
907     wt->inner_v[tot][0] = maxxi;
908     wt->inner_v[tot][1] = maxyi;
909
910     wt->outer_v[tot][0] = maxx;
911     wt->outer_v[tot][1] = maxy;
912
913     wt->inner_uv[tot][0] = 1.0f;
914     wt->inner_uv[tot][1] = 1.0f;
915
916     tot++;
917   }
918
919   /* corner left-top */
920   if (roundboxalign & UI_CNR_TOP_LEFT) {
921
922     for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
923       wt->inner_v[tot][0] = minxi + radi - veci[a][0];
924       wt->inner_v[tot][1] = maxyi - veci[a][1];
925
926       wt->outer_v[tot][0] = minx + rad - vec[a][0];
927       wt->outer_v[tot][1] = maxy - vec[a][1];
928
929       wt->inner_uv[tot][0] = facxi * (wt->inner_v[tot][0] - minxi);
930       wt->inner_uv[tot][1] = facyi * (wt->inner_v[tot][1] - minyi);
931     }
932   }
933   else {
934
935     wt->inner_v[tot][0] = minxi;
936     wt->inner_v[tot][1] = maxyi;
937
938     wt->outer_v[tot][0] = minx;
939     wt->outer_v[tot][1] = maxy;
940
941     wt->inner_uv[tot][0] = 0.0f;
942     wt->inner_uv[tot][1] = 1.0f;
943
944     tot++;
945   }
946
947   BLI_assert(tot <= WIDGET_SIZE_MAX);
948
949   wt->totvert = tot;
950 }
951
952 static void round_box_edges(uiWidgetBase *wt, int roundboxalign, const rcti *rect, float rad)
953 {
954   round_box__edges(wt, roundboxalign, rect, rad, rad - U.pixelsize);
955 }
956
957 /* -------------------------------------------------------------------- */
958 /** \name Shape Preset Mini API
959  * \{ */
960
961 /* based on button rect, return scaled array of triangles */
962 static void shape_preset_init_trias_ex(uiWidgetTrias *tria,
963                                        const rcti *rect,
964                                        float triasize,
965                                        char where,
966                                        /* input data */
967                                        const float verts[][2],
968                                        const int verts_tot,
969                                        const uint tris[][3],
970                                        const int tris_tot)
971 {
972   float centx, centy, sizex, sizey, minsize;
973   int a, i1 = 0, i2 = 1;
974
975   if (where == 'r' || where == 'l') {
976     minsize = BLI_rcti_size_y(rect);
977   }
978   else {
979     minsize = BLI_rcti_size_x(rect);
980   }
981
982   /* center position and size */
983   centx = (float)rect->xmin + 0.4f * minsize;
984   centy = (float)rect->ymin + 0.5f * minsize;
985   tria->size = sizex = sizey = -0.5f * triasize * minsize;
986
987   if (where == 'r') {
988     centx = (float)rect->xmax - 0.4f * minsize;
989     sizex = -sizex;
990   }
991   else if (where == 't') {
992     centx = (float)rect->xmin + 0.5f * minsize;
993     centy = (float)rect->ymax - 0.5f * minsize;
994     sizey = -sizey;
995     i2 = 0;
996     i1 = 1;
997   }
998   else if (where == 'b') {
999     centx = (float)rect->xmin + 0.5f * minsize;
1000     sizex = -sizex;
1001     i2 = 0;
1002     i1 = 1;
1003   }
1004
1005   for (a = 0; a < verts_tot; a++) {
1006     tria->vec[a][0] = sizex * verts[a][i1] + centx;
1007     tria->vec[a][1] = sizey * verts[a][i2] + centy;
1008   }
1009
1010   tria->center[0] = centx;
1011   tria->center[1] = centy;
1012
1013   tria->tot = tris_tot;
1014   tria->index = tris;
1015 }
1016
1017 static void shape_preset_init_number_arrows(uiWidgetTrias *tria,
1018                                             const rcti *rect,
1019                                             float triasize,
1020                                             char where)
1021 {
1022   tria->type = ROUNDBOX_TRIA_ARROWS;
1023   shape_preset_init_trias_ex(tria,
1024                              rect,
1025                              triasize,
1026                              where,
1027                              g_shape_preset_number_arrow_vert,
1028                              ARRAY_SIZE(g_shape_preset_number_arrow_vert),
1029                              g_shape_preset_number_arrow_face,
1030                              ARRAY_SIZE(g_shape_preset_number_arrow_face));
1031 }
1032
1033 static void shape_preset_init_hold_action(uiWidgetTrias *tria,
1034                                           const rcti *rect,
1035                                           float triasize,
1036                                           char where)
1037 {
1038   tria->type = ROUNDBOX_TRIA_HOLD_ACTION_ARROW;
1039   /* With the current changes to use batches for widget drawing, the code
1040    * below is doing almost nothing effectively. 'where' doesn't work either,
1041    * shader is currently hardcoded to work for the button triangle pointing
1042    * at the lower right. The same limitation applies to other trias as well.
1043    * XXX Should be addressed. */
1044   shape_preset_init_trias_ex(tria,
1045                              rect,
1046                              triasize,
1047                              where,
1048                              g_shape_preset_hold_action_vert,
1049                              ARRAY_SIZE(g_shape_preset_hold_action_vert),
1050                              g_shape_preset_hold_action_face,
1051                              ARRAY_SIZE(g_shape_preset_hold_action_face));
1052 }
1053
1054 static void shape_preset_init_scroll_circle(uiWidgetTrias *tria,
1055                                             const rcti *rect,
1056                                             float triasize,
1057                                             char where)
1058 {
1059   tria->type = ROUNDBOX_TRIA_SCROLL;
1060   shape_preset_init_trias_ex(tria,
1061                              rect,
1062                              triasize,
1063                              where,
1064                              g_shape_preset_scroll_circle_vert,
1065                              ARRAY_SIZE(g_shape_preset_scroll_circle_vert),
1066                              g_shape_preset_scroll_circle_face,
1067                              ARRAY_SIZE(g_shape_preset_scroll_circle_face));
1068 }
1069
1070 static void widget_draw_vertex_buffer(uint pos,
1071                                       uint col,
1072                                       int mode,
1073                                       const float quads_pos[WIDGET_SIZE_MAX][2],
1074                                       const uchar quads_col[WIDGET_SIZE_MAX][4],
1075                                       uint totvert)
1076 {
1077   immBegin(mode, totvert);
1078   for (int i = 0; i < totvert; ++i) {
1079     if (quads_col) {
1080       immAttr4ubv(col, quads_col[i]);
1081     }
1082     immVertex2fv(pos, quads_pos[i]);
1083   }
1084   immEnd();
1085 }
1086
1087 static void shape_preset_trias_from_rect_menu(uiWidgetTrias *tria, const rcti *rect)
1088 {
1089   float width = BLI_rcti_size_x(rect);
1090   float height = BLI_rcti_size_y(rect);
1091   float centx, centy, size;
1092
1093   tria->type = ROUNDBOX_TRIA_MENU;
1094
1095   /* Center position and size. */
1096   tria->center[0] = centx = rect->xmin + 0.52f * BLI_rcti_size_y(rect);
1097   tria->center[1] = centy = rect->ymin + 0.52f * BLI_rcti_size_y(rect);
1098   tria->size = size = 0.4f * height;
1099
1100   if (width > height * 1.1f) {
1101     /* For wider buttons align tighter to the right. */
1102     tria->center[0] = centx = rect->xmax - 0.32f * height;
1103   }
1104
1105   for (int a = 0; a < 6; a++) {
1106     tria->vec[a][0] = size * g_shape_preset_menu_arrow_vert[a][0] + centx;
1107     tria->vec[a][1] = size * g_shape_preset_menu_arrow_vert[a][1] + centy;
1108   }
1109
1110   tria->tot = 2;
1111   tria->index = g_shape_preset_menu_arrow_face;
1112 }
1113
1114 static void shape_preset_trias_from_rect_checkmark(uiWidgetTrias *tria, const rcti *rect)
1115 {
1116   float centx, centy, size;
1117
1118   tria->type = ROUNDBOX_TRIA_CHECK;
1119
1120   /* Center position and size. */
1121   tria->center[0] = centx = rect->xmin + 0.5f * BLI_rcti_size_y(rect);
1122   tria->center[1] = centy = rect->ymin + 0.5f * BLI_rcti_size_y(rect);
1123   tria->size = size = 0.5f * BLI_rcti_size_y(rect);
1124
1125   for (int a = 0; a < 6; a++) {
1126     tria->vec[a][0] = size * g_shape_preset_checkmark_vert[a][0] + centx;
1127     tria->vec[a][1] = size * g_shape_preset_checkmark_vert[a][1] + centy;
1128   }
1129
1130   tria->tot = 4;
1131   tria->index = g_shape_preset_checkmark_face;
1132 }
1133
1134 /** \} */
1135
1136 /* prepares shade colors */
1137 static void shadecolors4(
1138     uchar coltop[4], uchar coldown[4], const uchar *color, short shadetop, short shadedown)
1139 {
1140   coltop[0] = CLAMPIS(color[0] + shadetop, 0, 255);
1141   coltop[1] = CLAMPIS(color[1] + shadetop, 0, 255);
1142   coltop[2] = CLAMPIS(color[2] + shadetop, 0, 255);
1143   coltop[3] = color[3];
1144
1145   coldown[0] = CLAMPIS(color[0] + shadedown, 0, 255);
1146   coldown[1] = CLAMPIS(color[1] + shadedown, 0, 255);
1147   coldown[2] = CLAMPIS(color[2] + shadedown, 0, 255);
1148   coldown[3] = color[3];
1149 }
1150
1151 static void round_box_shade_col4_r(uchar r_col[4],
1152                                    const uchar col1[4],
1153                                    const uchar col2[4],
1154                                    const float fac)
1155 {
1156   const int faci = unit_float_to_uchar_clamp(fac);
1157   const int facm = 255 - faci;
1158
1159   r_col[0] = (faci * col1[0] + facm * col2[0]) / 256;
1160   r_col[1] = (faci * col1[1] + facm * col2[1]) / 256;
1161   r_col[2] = (faci * col1[2] + facm * col2[2]) / 256;
1162   r_col[3] = (faci * col1[3] + facm * col2[3]) / 256;
1163 }
1164
1165 static void widget_verts_to_triangle_strip(uiWidgetBase *wtb,
1166                                            const int totvert,
1167                                            float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2])
1168 {
1169   int a;
1170   for (a = 0; a < totvert; a++) {
1171     copy_v2_v2(triangle_strip[a * 2], wtb->outer_v[a]);
1172     copy_v2_v2(triangle_strip[a * 2 + 1], wtb->inner_v[a]);
1173   }
1174   copy_v2_v2(triangle_strip[a * 2], wtb->outer_v[0]);
1175   copy_v2_v2(triangle_strip[a * 2 + 1], wtb->inner_v[0]);
1176 }
1177
1178 static void widgetbase_outline(uiWidgetBase *wtb, uint pos)
1179 {
1180   float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2]; /* + 2 because the last pair is wrapped */
1181   widget_verts_to_triangle_strip(wtb, wtb->totvert, triangle_strip);
1182
1183   widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_STRIP, triangle_strip, NULL, wtb->totvert * 2 + 2);
1184 }
1185
1186 static void widgetbase_set_uniform_alpha_discard(uiWidgetBase *wtb,
1187                                                  const bool alpha_check,
1188                                                  const float discard_factor)
1189 {
1190   if (alpha_check) {
1191     wtb->uniform_params.alpha_discard = -discard_factor;
1192   }
1193   else {
1194     wtb->uniform_params.alpha_discard = discard_factor;
1195   }
1196 }
1197
1198 static void widgetbase_set_uniform_alpha_check(uiWidgetBase *wtb, const bool alpha_check)
1199 {
1200   const float discard_factor = fabs(wtb->uniform_params.alpha_discard);
1201   widgetbase_set_uniform_alpha_discard(wtb, alpha_check, discard_factor);
1202 }
1203
1204 static void widgetbase_set_uniform_discard_factor(uiWidgetBase *wtb, const float discard_factor)
1205 {
1206   bool alpha_check = wtb->uniform_params.alpha_discard < 0.0f;
1207   widgetbase_set_uniform_alpha_discard(wtb, alpha_check, discard_factor);
1208 }
1209
1210 static void widgetbase_set_uniform_colors_ubv(uiWidgetBase *wtb,
1211                                               const uchar *col1,
1212                                               const uchar *col2,
1213                                               const uchar *outline,
1214                                               const uchar *emboss,
1215                                               const uchar *tria,
1216                                               const bool alpha_check)
1217 {
1218   widgetbase_set_uniform_alpha_check(wtb, alpha_check);
1219   rgba_float_args_set_ch(wtb->uniform_params.color_inner1, col1[0], col1[1], col1[2], col1[3]);
1220   rgba_float_args_set_ch(wtb->uniform_params.color_inner2, col2[0], col2[1], col2[2], col2[3]);
1221   rgba_float_args_set_ch(
1222       wtb->uniform_params.color_outline, outline[0], outline[1], outline[2], outline[3]);
1223   rgba_float_args_set_ch(
1224       wtb->uniform_params.color_emboss, emboss[0], emboss[1], emboss[2], emboss[3]);
1225   rgba_float_args_set_ch(wtb->uniform_params.color_tria, tria[0], tria[1], tria[2], tria[3]);
1226 }
1227
1228 /* keep in sync with shader */
1229 #define MAX_WIDGET_BASE_BATCH 6
1230 #define MAX_WIDGET_PARAMETERS 11
1231
1232 static struct {
1233   GPUBatch *batch; /* Batch type */
1234   uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH];
1235   int count;
1236   bool enabled;
1237 } g_widget_base_batch = {0};
1238
1239 void UI_widgetbase_draw_cache_flush(void)
1240 {
1241   float checker_params[3] = {
1242       UI_ALPHA_CHECKER_DARK / 255.0f, UI_ALPHA_CHECKER_LIGHT / 255.0f, 8.0f};
1243
1244   if (g_widget_base_batch.count == 0) {
1245     return;
1246   }
1247
1248   GPUBatch *batch = g_widget_base_batch.batch;
1249   if (g_widget_base_batch.count == 1) {
1250     /* draw single */
1251     GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
1252     GPU_batch_uniform_4fv_array(
1253         batch, "parameters", MAX_WIDGET_PARAMETERS, (float *)g_widget_base_batch.params);
1254     GPU_batch_uniform_3fv(batch, "checkerColorAndSize", checker_params);
1255     GPU_batch_draw(batch);
1256   }
1257   else {
1258     GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE_INST);
1259     GPU_batch_uniform_4fv_array(batch,
1260                                 "parameters",
1261                                 MAX_WIDGET_PARAMETERS * MAX_WIDGET_BASE_BATCH,
1262                                 (float *)g_widget_base_batch.params);
1263     GPU_batch_uniform_3fv(batch, "checkerColorAndSize", checker_params);
1264     GPU_matrix_bind(batch->interface);
1265     GPU_batch_bind(batch);
1266     GPU_batch_draw_advanced(batch, 0, 0, 0, g_widget_base_batch.count);
1267
1268     GPU_batch_program_use_end(batch);
1269   }
1270   g_widget_base_batch.count = 0;
1271 }
1272
1273 void UI_widgetbase_draw_cache_begin(void)
1274 {
1275   BLI_assert(g_widget_base_batch.enabled == false);
1276   g_widget_base_batch.enabled = true;
1277 }
1278
1279 void UI_widgetbase_draw_cache_end(void)
1280 {
1281   BLI_assert(g_widget_base_batch.enabled == true);
1282   g_widget_base_batch.enabled = false;
1283
1284   GPU_blend(true);
1285
1286   UI_widgetbase_draw_cache_flush();
1287
1288   GPU_blend(false);
1289 }
1290
1291 static void draw_widgetbase_batch(GPUBatch *batch, uiWidgetBase *wtb)
1292 {
1293   wtb->uniform_params.tria1_size = wtb->tria1.size;
1294   wtb->uniform_params.tria2_size = wtb->tria2.size;
1295   copy_v2_v2(wtb->uniform_params.tria1_center, wtb->tria1.center);
1296   copy_v2_v2(wtb->uniform_params.tria2_center, wtb->tria2.center);
1297
1298   if (g_widget_base_batch.enabled) {
1299     if (g_widget_base_batch.batch == NULL) {
1300       g_widget_base_batch.batch = ui_batch_roundbox_widget_get(ROUNDBOX_TRIA_ARROWS);
1301     }
1302
1303     /* draw multi */
1304     if (batch != g_ui_batch_cache.roundbox_widget[ROUNDBOX_TRIA_NONE] &&
1305         batch != g_widget_base_batch.batch) {
1306       /* issue previous calls before changing batch type. */
1307       UI_widgetbase_draw_cache_flush();
1308       g_widget_base_batch.batch = batch;
1309     }
1310
1311     /* No need to change batch if tria is not visible. Just scale it to 0. */
1312     if (batch == g_ui_batch_cache.roundbox_widget[ROUNDBOX_TRIA_NONE]) {
1313       wtb->uniform_params.tria1_size = wtb->uniform_params.tria2_size = 0;
1314     }
1315
1316     g_widget_base_batch.params[g_widget_base_batch.count] = wtb->uniform_params;
1317     g_widget_base_batch.count++;
1318
1319     if (g_widget_base_batch.count == MAX_WIDGET_BASE_BATCH) {
1320       UI_widgetbase_draw_cache_flush();
1321     }
1322   }
1323   else {
1324     float checker_params[3] = {
1325         UI_ALPHA_CHECKER_DARK / 255.0f, UI_ALPHA_CHECKER_LIGHT / 255.0f, 8.0f};
1326     /* draw single */
1327     GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
1328     GPU_batch_uniform_4fv_array(
1329         batch, "parameters", MAX_WIDGET_PARAMETERS, (float *)&wtb->uniform_params);
1330     GPU_batch_uniform_3fv(batch, "checkerColorAndSize", checker_params);
1331     GPU_batch_draw(batch);
1332   }
1333 }
1334
1335 static void widgetbase_draw_ex(uiWidgetBase *wtb,
1336                                const uiWidgetColors *wcol,
1337                                bool show_alpha_checkers)
1338 {
1339   uchar inner_col1[4] = {0};
1340   uchar inner_col2[4] = {0};
1341   uchar emboss_col[4] = {0};
1342   uchar outline_col[4] = {0};
1343   uchar tria_col[4] = {0};
1344   /* For color widget. */
1345   if (wcol->shaded != 0) {
1346     show_alpha_checkers = false;
1347   }
1348
1349   GPU_blend(true);
1350
1351   /* backdrop non AA */
1352   if (wtb->draw_inner) {
1353     if (wcol->shaded == 0) {
1354       /* simple fill */
1355       inner_col1[0] = inner_col2[0] = wcol->inner[0];
1356       inner_col1[1] = inner_col2[1] = wcol->inner[1];
1357       inner_col1[2] = inner_col2[2] = wcol->inner[2];
1358       inner_col1[3] = inner_col2[3] = wcol->inner[3];
1359     }
1360     else {
1361       /* gradient fill */
1362       shadecolors4(inner_col1, inner_col2, wcol->inner, wcol->shadetop, wcol->shadedown);
1363     }
1364   }
1365
1366   if (wtb->draw_outline) {
1367     outline_col[0] = wcol->outline[0];
1368     outline_col[1] = wcol->outline[1];
1369     outline_col[2] = wcol->outline[2];
1370     outline_col[3] = wcol->outline[3] / WIDGET_AA_JITTER;
1371
1372     /* emboss bottom shadow */
1373     if (wtb->draw_emboss) {
1374       UI_GetThemeColor4ubv(TH_WIDGET_EMBOSS, emboss_col);
1375     }
1376   }
1377
1378   if (wtb->tria1.type != ROUNDBOX_TRIA_NONE) {
1379     tria_col[0] = wcol->item[0];
1380     tria_col[1] = wcol->item[1];
1381     tria_col[2] = wcol->item[2];
1382     tria_col[3] = (uchar)((float)wcol->item[3] / WIDGET_AA_JITTER);
1383   }
1384
1385   /* Draw everything in one drawcall */
1386   if (inner_col1[3] || inner_col2[3] || outline_col[3] || emboss_col[3] || tria_col[3] ||
1387       show_alpha_checkers) {
1388     widgetbase_set_uniform_colors_ubv(
1389         wtb, inner_col1, inner_col2, outline_col, emboss_col, tria_col, show_alpha_checkers);
1390
1391     GPUBatch *roundbox_batch = ui_batch_roundbox_widget_get(wtb->tria1.type);
1392     draw_widgetbase_batch(roundbox_batch, wtb);
1393   }
1394
1395   GPU_blend(false);
1396 }
1397
1398 static void widgetbase_draw(uiWidgetBase *wtb, const uiWidgetColors *wcol)
1399 {
1400   widgetbase_draw_ex(wtb, wcol, false);
1401 }
1402
1403 /* *********************** text/icon ************************************** */
1404
1405 #define UI_TEXT_CLIP_MARGIN (0.25f * U.widget_unit / but->block->aspect)
1406
1407 #define PREVIEW_PAD 4
1408
1409 static void widget_draw_preview(BIFIconID icon, float alpha, const rcti *rect)
1410 {
1411   int w, h, size;
1412
1413   if (icon == ICON_NONE) {
1414     return;
1415   }
1416
1417   w = BLI_rcti_size_x(rect);
1418   h = BLI_rcti_size_y(rect);
1419   size = MIN2(w, h);
1420   size -= PREVIEW_PAD * 2; /* padding */
1421
1422   if (size > 0) {
1423     int x = rect->xmin + w / 2 - size / 2;
1424     int y = rect->ymin + h / 2 - size / 2;
1425
1426     UI_icon_draw_preview(x, y, icon, 1.0f, alpha, size);
1427   }
1428 }
1429
1430 static int ui_but_draw_menu_icon(const uiBut *but)
1431 {
1432   return (but->flag & UI_BUT_ICON_SUBMENU) && (but->dt == UI_EMBOSS_PULLDOWN);
1433 }
1434
1435 /* icons have been standardized... and this call draws in untransformed coordinates */
1436
1437 static void widget_draw_icon(
1438     const uiBut *but, BIFIconID icon, float alpha, const rcti *rect, const uchar mono_color[4])
1439 {
1440   float xs = 0.0f, ys = 0.0f;
1441   float aspect, height;
1442
1443   if (but->flag & UI_BUT_ICON_PREVIEW) {
1444     GPU_blend(true);
1445     widget_draw_preview(icon, alpha, rect);
1446     GPU_blend(false);
1447     return;
1448   }
1449
1450   /* this icon doesn't need draw... */
1451   if (icon == ICON_BLANK1 && (but->flag & UI_BUT_ICON_SUBMENU) == 0) {
1452     return;
1453   }
1454
1455   aspect = but->block->aspect * U.inv_dpi_fac;
1456   height = ICON_DEFAULT_HEIGHT / aspect;
1457
1458   /* calculate blend color */
1459   if (ELEM(but->type, UI_BTYPE_TOGGLE, UI_BTYPE_ROW, UI_BTYPE_TOGGLE_N, UI_BTYPE_LISTROW)) {
1460     if (but->flag & UI_SELECT) {
1461       /* pass */
1462     }
1463     else if (but->flag & UI_ACTIVE) {
1464       /* pass */
1465     }
1466     else {
1467       alpha = 0.75f;
1468     }
1469   }
1470   else if ((but->type == UI_BTYPE_LABEL)) {
1471     /* extra feature allows more alpha blending */
1472     if (but->a1 == 1.0f) {
1473       alpha *= but->a2;
1474     }
1475   }
1476   else if (ELEM(but->type, UI_BTYPE_BUT)) {
1477     if (but->flag & UI_BUT_DISABLED) {
1478       alpha *= 0.5f;
1479     }
1480   }
1481
1482   GPU_blend(true);
1483
1484   if (icon && icon != ICON_BLANK1) {
1485     float ofs = 1.0f / aspect;
1486
1487     if (but->drawflag & UI_BUT_ICON_LEFT) {
1488       /* special case - icon_only pie buttons */
1489       if (ui_block_is_pie_menu(but->block) && !ELEM(but->type, UI_BTYPE_MENU, UI_BTYPE_POPOVER) &&
1490           but->str && but->str[0] == '\0') {
1491         xs = rect->xmin + 2.0f * ofs;
1492       }
1493       else if (but->dt == UI_EMBOSS_NONE || but->type == UI_BTYPE_LABEL) {
1494         xs = rect->xmin + 2.0f * ofs;
1495       }
1496       else {
1497         xs = rect->xmin + 4.0f * ofs;
1498       }
1499     }
1500     else {
1501       xs = (rect->xmin + rect->xmax - height) / 2.0f;
1502     }
1503     ys = (rect->ymin + rect->ymax - height) / 2.0f;
1504
1505     /* force positions to integers, for zoom levels near 1. draws icons crisp. */
1506     if (aspect > 0.95f && aspect < 1.05f) {
1507       xs = (int)(xs + 0.1f);
1508       ys = (int)(ys + 0.1f);
1509     }
1510
1511     /* Get theme color. */
1512     uchar color[4] = {mono_color[0], mono_color[1], mono_color[2], mono_color[3]};
1513     bool has_theme = UI_icon_get_theme_color(icon, color);
1514
1515     /* to indicate draggable */
1516     if (but->dragpoin && (but->flag & UI_ACTIVE)) {
1517       UI_icon_draw_ex(xs, ys, icon, aspect, 1.25f, 0.0f, color, has_theme);
1518     }
1519     else if ((but->flag & (UI_ACTIVE | UI_SELECT | UI_SELECT_DRAW))) {
1520       UI_icon_draw_ex(xs, ys, icon, aspect, alpha, 0.0f, color, has_theme);
1521     }
1522     else if (!UI_but_is_tool(but)) {
1523       if (has_theme) {
1524         alpha *= 0.8f;
1525       }
1526       UI_icon_draw_ex(xs, ys, icon, aspect, alpha, 0.0f, color, has_theme);
1527     }
1528     else {
1529       const bTheme *btheme = UI_GetTheme();
1530       const float desaturate = 1.0 - btheme->tui.icon_saturation;
1531       UI_icon_draw_ex(xs, ys, icon, aspect, alpha, desaturate, color, has_theme);
1532     }
1533   }
1534
1535   GPU_blend(false);
1536 }
1537
1538 static void widget_draw_submenu_tria(const uiBut *but,
1539                                      const rcti *rect,
1540                                      const uiWidgetColors *wcol)
1541 {
1542   const float aspect = but->block->aspect * U.inv_dpi_fac;
1543   const int tria_height = (int)(ICON_DEFAULT_HEIGHT / aspect);
1544   const int tria_width = (int)(ICON_DEFAULT_WIDTH / aspect) - 2 * U.pixelsize;
1545   const int xs = rect->xmax - tria_width;
1546   const int ys = (rect->ymin + rect->ymax - tria_height) / 2.0f;
1547   float col[4];
1548   rctf tria_rect;
1549
1550   rgba_uchar_to_float(col, wcol->text);
1551   col[3] *= 0.8f;
1552
1553   BLI_rctf_init(&tria_rect, xs, xs + tria_width, ys, ys + tria_height);
1554   BLI_rctf_scale(&tria_rect, 0.4f);
1555
1556   GPU_blend(true);
1557   UI_widgetbase_draw_cache_flush();
1558   GPU_blend(false);
1559   ui_draw_anti_tria_rect(&tria_rect, 'h', col);
1560 }
1561
1562 static void ui_text_clip_give_prev_off(uiBut *but, const char *str)
1563 {
1564   const char *prev_utf8 = BLI_str_find_prev_char_utf8(str, str + but->ofs);
1565   int bytes = str + but->ofs - prev_utf8;
1566
1567   but->ofs -= bytes;
1568 }
1569
1570 static void ui_text_clip_give_next_off(uiBut *but, const char *str)
1571 {
1572   const char *next_utf8 = BLI_str_find_next_char_utf8(str + but->ofs, NULL);
1573   int bytes = next_utf8 - (str + but->ofs);
1574
1575   but->ofs += bytes;
1576 }
1577
1578 /**
1579  * Helper.
1580  * This func assumes things like kerning handling have already been handled!
1581  * Return the length of modified (right-clipped + ellipsis) string.
1582  */
1583 static void ui_text_clip_right_ex(const uiFontStyle *fstyle,
1584                                   char *str,
1585                                   const size_t max_len,
1586                                   const float okwidth,
1587                                   const char *sep,
1588                                   const int sep_len,
1589                                   const float sep_strwidth,
1590                                   size_t *r_final_len)
1591 {
1592   float tmp;
1593   int l_end;
1594
1595   BLI_assert(str[0]);
1596
1597   /* If the trailing ellipsis takes more than 20% of all available width, just cut the string
1598    * (as using the ellipsis would remove even more useful chars, and we cannot show much
1599    * already!).
1600    */
1601   if (sep_strwidth / okwidth > 0.2f) {
1602     l_end = BLF_width_to_strlen(fstyle->uifont_id, str, max_len, okwidth, &tmp);
1603     str[l_end] = '\0';
1604     if (r_final_len) {
1605       *r_final_len = (size_t)l_end;
1606     }
1607   }
1608   else {
1609     l_end = BLF_width_to_strlen(fstyle->uifont_id, str, max_len, okwidth - sep_strwidth, &tmp);
1610     memcpy(str + l_end, sep, sep_len + 1); /* +1 for trailing '\0'. */
1611     if (r_final_len) {
1612       *r_final_len = (size_t)(l_end + sep_len);
1613     }
1614   }
1615 }
1616
1617 /**
1618  * Cut off the middle of the text to fit into the given width.
1619  *
1620  * \note in case this middle clipping would just remove a few chars,
1621  * it rather clips right, which is more readable.
1622  *
1623  * If rpart_sep is not Null, the part of str starting to first occurrence of rpart_sep
1624  * is preserved at all cost.
1625  * Useful for strings with shortcuts
1626  * (like 'AVeryLongFooBarLabelForMenuEntry|Ctrl O' -> 'AVeryLong...MenuEntry|Ctrl O').
1627  */
1628 float UI_text_clip_middle_ex(const uiFontStyle *fstyle,
1629                              char *str,
1630                              float okwidth,
1631                              const float minwidth,
1632                              const size_t max_len,
1633                              const char rpart_sep)
1634 {
1635   float strwidth;
1636
1637   /* Add some epsilon to OK width, avoids 'ellipsing' text that nearly fits!
1638    * Better to have a small piece of the last char cut out,
1639    * than two remaining chars replaced by an ellipsis... */
1640   okwidth += 1.0f + UI_DPI_FAC;
1641
1642   BLI_assert(str[0]);
1643
1644   /* need to set this first */
1645   UI_fontstyle_set(fstyle);
1646
1647   if (fstyle->kerning == 1) {
1648     /* for BLF_width */
1649     BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
1650   }
1651
1652   strwidth = BLF_width(fstyle->uifont_id, str, max_len);
1653
1654   if ((okwidth > 0.0f) && (strwidth > okwidth)) {
1655     /* utf8 two-dots leader '..' (shorter than ellipsis '...'),
1656      * some compilers complain with real literal string. */
1657     const char sep[] = {0xe2, 0x80, 0xA5, 0x0};
1658     const int sep_len = sizeof(sep) - 1;
1659     const float sep_strwidth = BLF_width(fstyle->uifont_id, sep, sep_len + 1);
1660     float parts_strwidth;
1661     size_t l_end;
1662
1663     char *rpart = NULL, rpart_buf[UI_MAX_DRAW_STR];
1664     float rpart_width = 0.0f;
1665     size_t rpart_len = 0;
1666     size_t final_lpart_len;
1667
1668     if (rpart_sep) {
1669       rpart = strrchr(str, rpart_sep);
1670
1671       if (rpart) {
1672         rpart_len = strlen(rpart);
1673         rpart_width = BLF_width(fstyle->uifont_id, rpart, rpart_len);
1674         okwidth -= rpart_width;
1675         strwidth -= rpart_width;
1676
1677         if (okwidth < 0.0f) {
1678           /* Not enough place for actual label, just display protected right part.
1679            * Here just for safety, should never happen in real life! */
1680           memmove(str, rpart, rpart_len + 1);
1681           rpart = NULL;
1682           okwidth += rpart_width;
1683           strwidth = rpart_width;
1684         }
1685       }
1686     }
1687
1688     parts_strwidth = (okwidth - sep_strwidth) / 2.0f;
1689
1690     if (rpart) {
1691       strcpy(rpart_buf, rpart);
1692       *rpart = '\0';
1693       rpart = rpart_buf;
1694     }
1695
1696     l_end = BLF_width_to_strlen(fstyle->uifont_id, str, max_len, parts_strwidth, NULL);
1697     if (l_end < 10 || min_ff(parts_strwidth, strwidth - okwidth) < minwidth) {
1698       /* If we really have no place, or we would clip a very small piece of string in the middle,
1699        * only show start of string.
1700        */
1701       ui_text_clip_right_ex(
1702           fstyle, str, max_len, okwidth, sep, sep_len, sep_strwidth, &final_lpart_len);
1703     }
1704     else {
1705       size_t r_offset, r_len;
1706
1707       r_offset = BLF_width_to_rstrlen(fstyle->uifont_id, str, max_len, parts_strwidth, NULL);
1708       r_len = strlen(str + r_offset) + 1; /* +1 for the trailing '\0'. */
1709
1710       if (l_end + sep_len + r_len + rpart_len > max_len) {
1711         /* Corner case, the str already takes all available mem,
1712          * and the ellipsis chars would actually add more chars.
1713          * Better to just trim one or two letters to the right in this case...
1714          * Note: with a single-char ellipsis, this should never happen! But better be safe
1715          * here...
1716          */
1717         ui_text_clip_right_ex(
1718             fstyle, str, max_len, okwidth, sep, sep_len, sep_strwidth, &final_lpart_len);
1719       }
1720       else {
1721         memmove(str + l_end + sep_len, str + r_offset, r_len);
1722         memcpy(str + l_end, sep, sep_len);
1723         /* -1 to remove trailing '\0'! */
1724         final_lpart_len = (size_t)(l_end + sep_len + r_len - 1);
1725
1726         while (BLF_width(fstyle->uifont_id, str, max_len) > okwidth) {
1727           /* This will happen because a lot of string width processing is done in integer pixels,
1728            * which can introduce a rather high error in the end (about 2 pixels or so).
1729            * Only one char removal shall ever be needed in real-life situation... */
1730           r_len--;
1731           final_lpart_len--;
1732           char *c = str + l_end + sep_len;
1733           memmove(c, c + 1, r_len);
1734         }
1735       }
1736     }
1737
1738     if (rpart) {
1739       /* Add back preserved right part to our shorten str. */
1740       memcpy(str + final_lpart_len, rpart, rpart_len + 1); /* +1 for trailing '\0'. */
1741       okwidth += rpart_width;
1742     }
1743
1744     strwidth = BLF_width(fstyle->uifont_id, str, max_len);
1745   }
1746
1747   if (fstyle->kerning == 1) {
1748     BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
1749   }
1750
1751   BLI_assert(strwidth <= okwidth);
1752
1753   return strwidth;
1754 }
1755
1756 /**
1757  * Wrapper around UI_text_clip_middle_ex.
1758  */
1759 static void ui_text_clip_middle(const uiFontStyle *fstyle, uiBut *but, const rcti *rect)
1760 {
1761   /* No margin for labels! */
1762   const int border = ELEM(but->type, UI_BTYPE_LABEL, UI_BTYPE_MENU, UI_BTYPE_POPOVER) ?
1763                          0 :
1764                          (int)(UI_TEXT_CLIP_MARGIN + 0.5f);
1765   const float okwidth = (float)max_ii(BLI_rcti_size_x(rect) - border, 0);
1766   const size_t max_len = sizeof(but->drawstr);
1767   const float minwidth = (float)(UI_DPI_ICON_SIZE) / but->block->aspect * 2.0f;
1768
1769   but->ofs = 0;
1770   but->strwidth = UI_text_clip_middle_ex(fstyle, but->drawstr, okwidth, minwidth, max_len, '\0');
1771 }
1772
1773 /**
1774  * Like #ui_text_clip_middle(), but protect/preserve at all cost
1775  * the right part of the string after sep.
1776  * Useful for strings with shortcuts
1777  * (like 'AVeryLongFooBarLabelForMenuEntry|Ctrl O' -> 'AVeryLong...MenuEntry|Ctrl O').
1778  */
1779 static void ui_text_clip_middle_protect_right(const uiFontStyle *fstyle,
1780                                               uiBut *but,
1781                                               const rcti *rect,
1782                                               const char rsep)
1783 {
1784   /* No margin for labels! */
1785   const int border = ELEM(but->type, UI_BTYPE_LABEL, UI_BTYPE_MENU, UI_BTYPE_POPOVER) ?
1786                          0 :
1787                          (int)(UI_TEXT_CLIP_MARGIN + 0.5f);
1788   const float okwidth = (float)max_ii(BLI_rcti_size_x(rect) - border, 0);
1789   const size_t max_len = sizeof(but->drawstr);
1790   const float minwidth = (float)(UI_DPI_ICON_SIZE) / but->block->aspect * 2.0f;
1791
1792   but->ofs = 0;
1793   but->strwidth = UI_text_clip_middle_ex(fstyle, but->drawstr, okwidth, minwidth, max_len, rsep);
1794 }
1795
1796 /**
1797  * Cut off the text, taking into account the cursor location (text display while editing).
1798  */
1799 static void ui_text_clip_cursor(const uiFontStyle *fstyle, uiBut *but, const rcti *rect)
1800 {
1801   const int border = (int)(UI_TEXT_CLIP_MARGIN + 0.5f);
1802   const int okwidth = max_ii(BLI_rcti_size_x(rect) - border, 0);
1803
1804   BLI_assert(but->editstr && but->pos >= 0);
1805
1806   /* need to set this first */
1807   UI_fontstyle_set(fstyle);
1808
1809   if (fstyle->kerning == 1) {
1810     /* for BLF_width */
1811     BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
1812   }
1813
1814   /* define ofs dynamically */
1815   if (but->ofs > but->pos) {
1816     but->ofs = but->pos;
1817   }
1818
1819   if (BLF_width(fstyle->uifont_id, but->editstr, INT_MAX) <= okwidth) {
1820     but->ofs = 0;
1821   }
1822
1823   but->strwidth = BLF_width(fstyle->uifont_id, but->editstr + but->ofs, INT_MAX);
1824
1825   if (but->strwidth > okwidth) {
1826     int len = strlen(but->editstr);
1827
1828     while (but->strwidth > okwidth) {
1829       float width;
1830
1831       /* string position of cursor */
1832       width = BLF_width(fstyle->uifont_id, but->editstr + but->ofs, (but->pos - but->ofs));
1833
1834       /* if cursor is at 20 pixels of right side button we clip left */
1835       if (width > okwidth - 20) {
1836         ui_text_clip_give_next_off(but, but->editstr);
1837       }
1838       else {
1839         int bytes;
1840         /* shift string to the left */
1841         if (width < 20 && but->ofs > 0) {
1842           ui_text_clip_give_prev_off(but, but->editstr);
1843         }
1844         bytes = BLI_str_utf8_size(BLI_str_find_prev_char_utf8(but->editstr, but->editstr + len));
1845         if (bytes == -1) {
1846           bytes = 1;
1847         }
1848         len -= bytes;
1849       }
1850
1851       but->strwidth = BLF_width(fstyle->uifont_id, but->editstr + but->ofs, len - but->ofs);
1852
1853       if (but->strwidth < 10) {
1854         break;
1855       }
1856     }
1857   }
1858
1859   if (fstyle->kerning == 1) {
1860     BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
1861   }
1862 }
1863
1864 /**
1865  * Cut off the end of text to fit into the width of \a rect.
1866  *
1867  * \note deals with ': ' especially for number buttons
1868  */
1869 static void ui_text_clip_right_label(const uiFontStyle *fstyle, uiBut *but, const rcti *rect)
1870 {
1871   const int border = UI_TEXT_CLIP_MARGIN + 1;
1872   const int okwidth = max_ii(BLI_rcti_size_x(rect) - border, 0);
1873   char *cpoin = NULL;
1874   int drawstr_len = strlen(but->drawstr);
1875   const char *cpend = but->drawstr + drawstr_len;
1876
1877   /* need to set this first */
1878   UI_fontstyle_set(fstyle);
1879
1880   if (fstyle->kerning == 1) {
1881     /* for BLF_width */
1882     BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
1883   }
1884
1885   but->strwidth = BLF_width(fstyle->uifont_id, but->drawstr, sizeof(but->drawstr));
1886   but->ofs = 0;
1887
1888   /* First shorten num-buttons eg,
1889    *   Translucency: 0.000
1890    * becomes
1891    *   Trans: 0.000
1892    */
1893
1894   /* find the space after ':' separator */
1895   cpoin = strrchr(but->drawstr, ':');
1896
1897   if (cpoin && (cpoin < cpend - 2)) {
1898     char *cp2 = cpoin;
1899
1900     /* chop off the leading text, starting from the right */
1901     while (but->strwidth > okwidth && cp2 > but->drawstr) {
1902       const char *prev_utf8 = BLI_str_find_prev_char_utf8(but->drawstr, cp2);
1903       int bytes = cp2 - prev_utf8;
1904
1905       /* shift the text after and including cp2 back by 1 char,
1906        * +1 to include null terminator */
1907       memmove(cp2 - bytes, cp2, drawstr_len + 1);
1908       cp2 -= bytes;
1909
1910       drawstr_len -= bytes;
1911       // BLI_assert(strlen(but->drawstr) == drawstr_len);
1912
1913       but->strwidth = BLF_width(
1914           fstyle->uifont_id, but->drawstr + but->ofs, sizeof(but->drawstr) - but->ofs);
1915       if (but->strwidth < 10) {
1916         break;
1917       }
1918     }
1919
1920     /* after the leading text is gone, chop off the : and following space, with ofs */
1921     while ((but->strwidth > okwidth) && (but->ofs < 2)) {
1922       ui_text_clip_give_next_off(but, but->drawstr);
1923       but->strwidth = BLF_width(
1924           fstyle->uifont_id, but->drawstr + but->ofs, sizeof(but->drawstr) - but->ofs);
1925       if (but->strwidth < 10) {
1926         break;
1927       }
1928     }
1929   }
1930
1931   /* Now just remove trailing chars */
1932   /* once the label's gone, chop off the least significant digits */
1933   if (but->strwidth > okwidth) {
1934     float strwidth;
1935     drawstr_len = BLF_width_to_strlen(fstyle->uifont_id,
1936                                       but->drawstr + but->ofs,
1937                                       drawstr_len - but->ofs,
1938                                       okwidth,
1939                                       &strwidth) +
1940                   but->ofs;
1941     but->strwidth = strwidth;
1942     but->drawstr[drawstr_len] = 0;
1943   }
1944
1945   if (fstyle->kerning == 1) {
1946     BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
1947   }
1948 }
1949
1950 #ifdef WITH_INPUT_IME
1951 static void widget_draw_text_ime_underline(const uiFontStyle *fstyle,
1952                                            const uiWidgetColors *wcol,
1953                                            const uiBut *but,
1954                                            const rcti *rect,
1955                                            const wmIMEData *ime_data,
1956                                            const char *drawstr)
1957 {
1958   int ofs_x, width;
1959   int rect_x = BLI_rcti_size_x(rect);
1960   int sel_start = ime_data->sel_start, sel_end = ime_data->sel_end;
1961   float fcol[4];
1962
1963   if (drawstr[0] != 0) {
1964     if (but->pos >= but->ofs) {
1965       ofs_x = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->pos - but->ofs);
1966     }
1967     else {
1968       ofs_x = 0;
1969     }
1970
1971     width = BLF_width(
1972         fstyle->uifont_id, drawstr + but->ofs, ime_data->composite_len + but->pos - but->ofs);
1973
1974     rgba_uchar_to_float(fcol, wcol->text);
1975     UI_draw_text_underline(rect->xmin + ofs_x,
1976                            rect->ymin + 6 * U.pixelsize,
1977                            min_ii(width, rect_x - 2) - ofs_x,
1978                            1,
1979                            fcol);
1980
1981     /* draw the thick line */
1982     if (sel_start != -1 && sel_end != -1) {
1983       sel_end -= sel_start;
1984       sel_start += but->pos;
1985
1986       if (sel_start >= but->ofs) {
1987         ofs_x = BLF_width(fstyle->uifont_id, drawstr + but->ofs, sel_start - but->ofs);
1988       }
1989       else {
1990         ofs_x = 0;
1991       }
1992
1993       width = BLF_width(fstyle->uifont_id, drawstr + but->ofs, sel_end + sel_start - but->ofs);
1994
1995       UI_draw_text_underline(rect->xmin + ofs_x,
1996                              rect->ymin + 6 * U.pixelsize,
1997                              min_ii(width, rect_x - 2) - ofs_x,
1998                              2,
1999                              fcol);
2000     }
2001   }
2002 }
2003 #endif /* WITH_INPUT_IME */
2004
2005 static void widget_draw_text(const uiFontStyle *fstyle,
2006                              const uiWidgetColors *wcol,
2007                              uiBut *but,
2008                              rcti *rect)
2009 {
2010   int drawstr_left_len = UI_MAX_DRAW_STR;
2011   const char *drawstr = but->drawstr;
2012   const char *drawstr_right = NULL;
2013   bool use_right_only = false;
2014
2015 #ifdef WITH_INPUT_IME
2016   const wmIMEData *ime_data;
2017 #endif
2018
2019   UI_fontstyle_set(fstyle);
2020
2021   eFontStyle_Align align;
2022   if (but->editstr || (but->drawflag & UI_BUT_TEXT_LEFT)) {
2023     align = UI_STYLE_TEXT_LEFT;
2024   }
2025   else if (but->drawflag & UI_BUT_TEXT_RIGHT) {
2026     align = UI_STYLE_TEXT_RIGHT;
2027   }
2028   else {
2029     align = UI_STYLE_TEXT_CENTER;
2030   }
2031
2032   if (fstyle->kerning == 1) {
2033     /* for BLF_width */
2034     BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
2035   }
2036
2037   /* Special case: when we're entering text for multiple buttons,
2038    * don't draw the text for any of the multi-editing buttons */
2039   if (UNLIKELY(but->flag & UI_BUT_DRAG_MULTI)) {
2040     uiBut *but_edit = ui_but_drag_multi_edit_get(but);
2041     if (but_edit) {
2042       drawstr = but_edit->editstr;
2043       align = UI_STYLE_TEXT_LEFT;
2044     }
2045   }
2046   else {
2047     if (but->editstr) {
2048       /* max length isn't used in this case,
2049        * we rely on string being NULL terminated. */
2050       drawstr_left_len = INT_MAX;
2051
2052 #ifdef WITH_INPUT_IME
2053       /* FIXME, IME is modifying 'const char *drawstr! */
2054       ime_data = ui_but_ime_data_get(but);
2055
2056       if (ime_data && ime_data->composite_len) {
2057         /* insert composite string into cursor pos */
2058         BLI_snprintf((char *)drawstr,
2059                      UI_MAX_DRAW_STR,
2060                      "%s%s%s",
2061                      but->editstr,
2062                      ime_data->str_composite,
2063                      but->editstr + but->pos);
2064       }
2065       else
2066 #endif
2067       {
2068         drawstr = but->editstr;
2069       }
2070     }
2071   }
2072
2073   /* text button selection, cursor, composite underline */
2074   if (but->editstr && but->pos != -1) {
2075     int but_pos_ofs;
2076     int tx, ty;
2077
2078     /* text button selection */
2079     if ((but->selend - but->selsta) > 0) {
2080       int selsta_draw, selwidth_draw;
2081
2082       if (drawstr[0] != 0) {
2083         /* We are drawing on top of widget bases. Flush cache. */
2084         GPU_blend(true);
2085         UI_widgetbase_draw_cache_flush();
2086         GPU_blend(false);
2087
2088         if (but->selsta >= but->ofs) {
2089           selsta_draw = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->selsta - but->ofs);
2090         }
2091         else {
2092           selsta_draw = 0;
2093         }
2094
2095         selwidth_draw = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->selend - but->ofs);
2096
2097         uint pos = GPU_vertformat_attr_add(
2098             immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
2099         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2100
2101         immUniformColor4ubv(wcol->item);
2102         immRecti(pos,
2103                  rect->xmin + selsta_draw,
2104                  rect->ymin + 2,
2105                  min_ii(rect->xmin + selwidth_draw, rect->xmax - 2),
2106                  rect->ymax - 2);
2107
2108         immUnbindProgram();
2109       }
2110     }
2111
2112     /* text cursor */
2113     but_pos_ofs = but->pos;
2114
2115 #ifdef WITH_INPUT_IME
2116     /* if is ime compositing, move the cursor */
2117     if (ime_data && ime_data->composite_len && ime_data->cursor_pos != -1) {
2118       but_pos_ofs += ime_data->cursor_pos;
2119     }
2120 #endif
2121
2122     if (but->pos >= but->ofs) {
2123       int t;
2124       if (drawstr[0] != 0) {
2125         t = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but_pos_ofs - but->ofs);
2126       }
2127       else {
2128         t = 0;
2129       }
2130       /* We are drawing on top of widget bases. Flush cache. */
2131       GPU_blend(true);
2132       UI_widgetbase_draw_cache_flush();
2133       GPU_blend(false);
2134
2135       uint pos = GPU_vertformat_attr_add(
2136           immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
2137       immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2138
2139       immUniformColor3f(0.2f, 0.6f, 0.9f);
2140
2141       tx = rect->xmin + t + 2;
2142       ty = rect->ymin + 2;
2143
2144       /* draw cursor */
2145       immRecti(pos, rect->xmin + t, ty, tx, rect->ymax - 2);
2146
2147       immUnbindProgram();
2148     }
2149
2150 #ifdef WITH_INPUT_IME
2151     if (ime_data && ime_data->composite_len) {
2152       /* ime cursor following */
2153       if (but->pos >= but->ofs) {
2154         ui_but_ime_reposition(but, tx + 5, ty + 3, false);
2155       }
2156
2157       /* composite underline */
2158       widget_draw_text_ime_underline(fstyle, wcol, but, rect, ime_data, drawstr);
2159     }
2160 #endif
2161   }
2162
2163   if (fstyle->kerning == 1) {
2164     BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
2165   }
2166
2167 #if 0
2168   ui_rasterpos_safe(x, y, but->aspect);
2169   transopts = ui_translate_buttons();
2170 #endif
2171
2172   /* cut string in 2 parts - only for menu entries */
2173   if ((but->drawflag & UI_BUT_HAS_SHORTCUT) && (but->editstr == NULL)) {
2174     if (but->flag & UI_BUT_HAS_SEP_CHAR) {
2175       drawstr_right = strrchr(drawstr, UI_SEP_CHAR);
2176       if (drawstr_right) {
2177         drawstr_left_len = (drawstr_right - drawstr);
2178         drawstr_right++;
2179       }
2180     }
2181   }
2182
2183 #ifdef USE_NUMBUTS_LR_ALIGN
2184   if (!drawstr_right && (but->drawflag & UI_BUT_TEXT_LEFT) &&
2185       ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER) &&
2186       /* if we're editing or multi-drag (fake editing), then use left alignment */
2187       (but->editstr == NULL) && (drawstr == but->drawstr)) {
2188     drawstr_right = strrchr(drawstr + but->ofs, ':');
2189     if (drawstr_right) {
2190       drawstr_right++;
2191       drawstr_left_len = (drawstr_right - drawstr);
2192
2193       while (*drawstr_right == ' ') {
2194         drawstr_right++;
2195       }
2196     }
2197     else {
2198       /* no prefix, even so use only cpoin */
2199       drawstr_right = drawstr + but->ofs;
2200       use_right_only = true;
2201     }
2202   }
2203 #endif
2204
2205   if (!use_right_only) {
2206     /* for underline drawing */
2207     float font_xofs, font_yofs;
2208
2209     int drawlen = (drawstr_left_len == INT_MAX) ? strlen(drawstr + but->ofs) :
2210                                                   (drawstr_left_len - but->ofs);
2211
2212     if (drawlen > 0) {
2213       UI_fontstyle_draw_ex(fstyle,
2214                            rect,
2215                            drawstr + but->ofs,
2216                            wcol->text,
2217                            &(struct uiFontStyleDraw_Params){
2218                                .align = align,
2219                            },
2220                            drawlen,
2221                            &font_xofs,
2222                            &font_yofs);
2223
2224       if (but->menu_key != '\0') {
2225         char fixedbuf[128];
2226         const char *str;
2227
2228         BLI_strncpy(fixedbuf, drawstr + but->ofs, min_ii(sizeof(fixedbuf), drawlen));
2229
2230         str = strchr(fixedbuf, but->menu_key - 32); /* upper case */
2231         if (str == NULL) {
2232           str = strchr(fixedbuf, but->menu_key);
2233         }
2234
2235         if (str) {
2236           int ul_index = -1;
2237           float ul_advance;
2238
2239           ul_index = (int)(str - fixedbuf);
2240
2241           if (fstyle->kerning == 1) {
2242             BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
2243           }
2244
2245           fixedbuf[ul_index] = '\0';
2246           ul_advance = BLF_width(fstyle->uifont_id, fixedbuf, ul_index) + (1.0f * UI_DPI_FAC);
2247
2248           BLF_position(fstyle->uifont_id,
2249                        rect->xmin + font_xofs + ul_advance,
2250                        rect->ymin + font_yofs,
2251                        0.0f);
2252           BLF_color4ubv(fstyle->uifont_id, wcol->text);
2253           BLF_draw(fstyle->uifont_id, "_", 2);
2254
2255           if (fstyle->kerning == 1) {
2256             BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
2257           }
2258         }
2259       }
2260     }
2261   }
2262
2263   /* part text right aligned */
2264   if (drawstr_right) {
2265     uchar col[4];
2266     copy_v4_v4_uchar(col, wcol->text);
2267     if (but->drawflag & UI_BUT_HAS_SHORTCUT) {
2268       col[3] *= 0.5f;
2269     }
2270
2271     rect->xmax -= UI_TEXT_CLIP_MARGIN;
2272     UI_fontstyle_draw(fstyle,
2273                       rect,
2274                       drawstr_right,
2275                       col,
2276                       &(struct uiFontStyleDraw_Params){
2277                           .align = UI_STYLE_TEXT_RIGHT,
2278                       });
2279   }
2280 }
2281
2282 static BIFIconID widget_icon_id(uiBut *but)
2283 {
2284   if (!(but->flag & UI_HAS_ICON)) {
2285     return ICON_NONE;
2286   }
2287
2288   /* Consecutive icons can be toggle between. */
2289   if (but->drawflag & UI_BUT_ICON_REVERSE) {
2290     return but->icon - but->iconadd;
2291   }
2292   else {
2293     return but->icon + but->iconadd;
2294   }
2295 }
2296
2297 /* draws text and icons for buttons */
2298 static void widget_draw_text_icon(const uiFontStyle *fstyle,
2299                                   const uiWidgetColors *wcol,
2300                                   uiBut *but,
2301                                   rcti *rect)
2302 {
2303   const uiButExtraIconType extra_icon_type = ui_but_icon_extra_get(but);
2304   const bool show_menu_icon = ui_but_draw_menu_icon(but);
2305   float alpha = (float)wcol->text[3] / 255.0f;
2306   char password_str[UI_MAX_DRAW_STR];
2307
2308   ui_but_text_password_hide(password_str, but, false);
2309
2310   /* check for button text label */
2311   if (ELEM(but->type, UI_BTYPE_MENU, UI_BTYPE_POPOVER) && (but->flag & UI_BUT_NODE_LINK)) {
2312     rcti temp = *rect;
2313     temp.xmin = rect->xmax - BLI_rcti_size_y(rect) - 1;
2314     widget_draw_icon(but, ICON_LAYER_USED, alpha, &temp, wcol->text);
2315     rect->xmax = temp.xmin;
2316   }
2317
2318   /* If there's an icon too (made with uiDefIconTextBut) then draw the icon
2319    * and offset the text label to accommodate it */
2320
2321   /* Big previews with optional text label below */
2322   if (but->flag & UI_BUT_ICON_PREVIEW && ui_block_is_menu(but->block)) {
2323     const BIFIconID icon = widget_icon_id(but);
2324     int icon_size = BLI_rcti_size_y(rect);
2325     int text_size = 0;
2326
2327     /* This is a bit britle, but avoids adding an 'UI_BUT_HAS_LABEL' flag to but... */
2328     if (icon_size > BLI_rcti_size_x(rect)) {
2329       /* button is not square, it has extra height for label */
2330       text_size = UI_UNIT_Y;
2331       icon_size -= text_size;
2332     }
2333
2334     /* draw icon in rect above the space reserved for the label */
2335     rect->ymin += text_size;
2336     GPU_blend(true);
2337     widget_draw_preview(icon, alpha, rect);
2338     GPU_blend(false);
2339
2340     /* offset rect to draw label in */
2341     rect->ymin -= text_size;
2342     rect->ymax -= icon_size;
2343
2344     /* vertically centering text */
2345     rect->ymin += UI_UNIT_Y / 2;
2346   }
2347   /* Icons on the left with optional text label on the right */
2348   else if (but->flag & UI_HAS_ICON || show_menu_icon) {
2349     const bool is_tool = UI_but_is_tool(but);
2350
2351     /* XXX add way to draw icons at a different size!
2352      * Use small icons for popup. */
2353 #ifdef USE_UI_TOOLBAR_HACK
2354     const float aspect_orig = but->block->aspect;
2355     if (is_tool && (but->block->flag & UI_BLOCK_POPOVER)) {
2356       but->block->aspect *= 2.0f;
2357     }
2358 #endif
2359
2360     const BIFIconID icon = widget_icon_id(but);
2361     int icon_size_init = is_tool ? ICON_DEFAULT_HEIGHT_TOOLBAR : ICON_DEFAULT_HEIGHT;
2362     const float icon_size = icon_size_init / (but->block->aspect * U.inv_dpi_fac);
2363     const float icon_padding = 2 * UI_DPI_FAC;
2364
2365 #ifdef USE_UI_TOOLBAR_HACK
2366     if (is_tool) {
2367       /* pass (even if its a menu toolbar) */
2368       but->drawflag |= UI_BUT_TEXT_LEFT;
2369       but->drawflag |= UI_BUT_ICON_LEFT;
2370     }
2371 #endif
2372
2373     /* menu item - add some more padding so menus don't feel cramped. it must
2374      * be part of the button so that this area is still clickable */
2375     if (is_tool) {
2376       /* pass (even if its a menu toolbar) */
2377     }
2378     else if (ui_block_is_pie_menu(but->block)) {
2379       if (but->dt == UI_EMBOSS_RADIAL) {
2380         rect->xmin += 0.3f * U.widget_unit;
2381       }
2382     }
2383     else if (ui_block_is_menu(but->block)) {
2384       rect->xmin += 0.2f * U.widget_unit;
2385     }
2386
2387     widget_draw_icon(but, icon, alpha, rect, wcol->text);
2388     if (show_menu_icon) {
2389       BLI_assert(but->block->content_hints & UI_BLOCK_CONTAINS_SUBMENU_BUT);
2390       widget_draw_submenu_tria(but, rect, wcol);
2391     }
2392
2393 #ifdef USE_UI_TOOLBAR_HACK
2394     but->block->aspect = aspect_orig;
2395 #endif
2396
2397     rect->xmin += icon_size + icon_padding;
2398   }
2399
2400   int text_padding = (UI_TEXT_MARGIN_X * U.widget_unit) / but->block->aspect;
2401   if (but->editstr) {
2402     rect->xmin += text_padding;
2403   }
2404   else if (but->flag & UI_BUT_DRAG_MULTI) {
2405     bool text_is_edited = ui_but_drag_multi_edit_get(but) != NULL;
2406     if (text_is_edited) {
2407       rect->xmin += text_padding;
2408     }
2409   }
2410   else if (but->drawflag & UI_BUT_TEXT_LEFT) {
2411     rect->xmin += text_padding;
2412   }
2413   else if (but->drawflag & UI_BUT_TEXT_RIGHT) {
2414     rect->xmax -= text_padding;
2415   }
2416
2417   /* Menu contains sub-menu items with triangle icon on their right. Shortcut
2418    * strings should be drawn with some padding to the right then. */
2419   if (ui_block_is_menu(but->block) &&
2420       (but->block->content_hints & UI_BLOCK_CONTAINS_SUBMENU_BUT)) {
2421     rect->xmax -= UI_MENU_SUBMENU_PADDING;
2422   }
2423
2424   /* extra icons, e.g. 'x' icon to clear text or icon for eyedropper */
2425   if (extra_icon_type != UI_BUT_ICONEXTRA_NONE) {
2426     rcti temp = *rect;
2427
2428     temp.xmin = temp.xmax - (BLI_rcti_size_y(rect) * 1.08f);
2429
2430     if (extra_icon_type == UI_BUT_ICONEXTRA_CLEAR) {
2431       widget_draw_icon(but, ICON_PANEL_CLOSE, alpha, &temp, wcol->text);
2432     }
2433     else if (extra_icon_type == UI_BUT_ICONEXTRA_EYEDROPPER) {
2434       widget_draw_icon(but, ICON_EYEDROPPER, alpha, &temp, wcol->text);
2435     }
2436     else {
2437       BLI_assert(0);
2438     }
2439
2440     rect->xmax -= ICON_SIZE_FROM_BUTRECT(rect);
2441   }
2442
2443   /* clip but->drawstr to fit in available space */
2444   if (but->editstr && but->pos >= 0) {
2445     ui_text_clip_cursor(fstyle, but, rect);
2446   }
2447   else if (but->drawstr[0] == '\0') {
2448     /* bypass text clipping on icon buttons */
2449     but->ofs = 0;
2450     but->strwidth = 0;
2451   }
2452   else if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER)) {
2453     ui_text_clip_right_label(fstyle, but, rect);
2454   }
2455   else if (but->flag & UI_BUT_HAS_SEP_CHAR) {
2456     /* Clip middle, but protect in all case right part containing the shortcut, if any. */
2457     ui_text_clip_middle_protect_right(fstyle, but, rect, UI_SEP_CHAR);
2458   }
2459   else {
2460     ui_text_clip_middle(fstyle, but, rect);
2461   }
2462
2463   /* always draw text for textbutton cursor */
2464   widget_draw_text(fstyle, wcol, but, rect);
2465
2466   ui_but_text_password_hide(password_str, but, true);
2467
2468   /* if a widget uses font shadow it has to be deactivated now */
2469   BLF_disable(fstyle->uifont_id, BLF_SHADOW);
2470 }
2471
2472 #undef UI_TEXT_CLIP_MARGIN
2473
2474 /* *********************** widget types ************************************* */
2475
2476 /* ************ button callbacks, state ***************** */
2477
2478 static void widget_state_blend(uchar cp[3], const uchar cpstate[3], const float fac)
2479 {
2480   if (fac != 0.0f) {
2481     cp[0] = (int)((1.0f - fac) * cp[0] + fac * cpstate[0]);
2482     cp[1] = (int)((1.0f - fac) * cp[1] + fac * cpstate[1]);
2483     cp[2] = (int)((1.0f - fac) * cp[2] + fac * cpstate[2]);
2484   }
2485 }
2486
2487 /* put all widget colors on half alpha, use local storage */
2488 static void ui_widget_color_disabled(uiWidgetType *wt)
2489 {
2490   static uiWidgetColors wcol_theme_s;
2491
2492   wcol_theme_s = *wt->wcol_theme;
2493
2494   wcol_theme_s.outline[3] *= 0.5;
2495   wcol_theme_s.inner[3] *= 0.5;
2496   wcol_theme_s.inner_sel[3] *= 0.5;
2497   wcol_theme_s.item[3] *= 0.5;
2498   wcol_theme_s.text[3] *= 0.5;
2499   wcol_theme_s.text_sel[3] *= 0.5;
2500
2501   wt->wcol_theme = &wcol_theme_s;
2502 }
2503
2504 static void rgb_tint(uchar cp[3], int tint)
2505 {
2506   cp[0] = clamp_i(cp[0] + tint, 0, 255);
2507   cp[1] = clamp_i(cp[1] + tint, 0, 255);
2508   cp[2] = clamp_i(cp[2] + tint, 0, 255);
2509 }
2510
2511 static void rgb_ensure_contrast(uchar cp[3], const uchar cp_other[3], int contrast)
2512 {
2513   BLI_assert(contrast > 0);
2514   const int item_value = rgb_to_grayscale_byte(cp);
2515   const int inner_value = rgb_to_grayscale_byte(cp_other);
2516   const int delta = item_value - inner_value;
2517   if (delta >= 0) {
2518     if (contrast > delta) {
2519       rgb_tint(cp, contrast - delta);
2520     }
2521   }
2522   else {
2523     if (contrast > -delta) {
2524       rgb_tint(cp, -contrast - delta);
2525     }
2526   }
2527 }
2528
2529 static void widget_active_color(uchar cp[3])
2530 {
2531   cp[0] = cp[0] >= 240 ? 255 : cp[0] + 15;
2532   cp[1] = cp[1] >= 240 ? 255 : cp[1] + 15;
2533   cp[2] = cp[2] >= 240 ? 255 : cp[2] + 15;
2534 }
2535
2536 static const uchar *widget_color_blend_from_flags(const uiWidgetStateColors *wcol_state,
2537                                                   int state,
2538                                                   int drawflag)
2539 {
2540   if (drawflag & UI_BUT_ANIMATED_CHANGED) {
2541     return wcol_state->inner_changed_sel;
2542   }
2543   if (state & UI_BUT_ANIMATED_KEY) {
2544     return wcol_state->inner_key_sel;
2545   }
2546   if (state & UI_BUT_ANIMATED) {
2547     return wcol_state->inner_anim_sel;
2548   }
2549   if (state & UI_BUT_DRIVEN) {
2550     return wcol_state->inner_driven_sel;
2551   }
2552   if (state & UI_BUT_OVERRIDEN) {
2553     return wcol_state->inner_overridden_sel;
2554   }
2555   return NULL;
2556 }
2557
2558 /* copy colors from theme, and set changes in it based on state */
2559 static void widget_state(uiWidgetType *wt, int state, int drawflag)
2560 {
2561   uiWidgetStateColors *wcol_state = wt->wcol_state;
2562
2563   if ((state & UI_BUT_LIST_ITEM) && !(state & UI_STATE_TEXT_INPUT)) {
2564     /* Override default widget's colors. */
2565     bTheme *btheme = UI_GetTheme();
2566     wt->wcol_theme = &btheme->tui.wcol_list_item;
2567
2568     if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) {
2569       ui_widget_color_disabled(wt);
2570     }
2571   }
2572
2573   wt->wcol = *(wt->wcol_theme);
2574
2575   const uchar *color_blend = widget_color_blend_from_flags(wcol_state, state, drawflag);
2576
2577   if (state & UI_SELECT) {
2578     copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel);
2579     if (color_blend != NULL) {
2580       widget_state_blend(wt->wcol.inner, color_blend, wcol_state->blend);
2581     }
2582
2583     copy_v3_v3_uchar(wt->wcol.text, wt->wcol.text_sel);
2584
2585     if (state & UI_SELECT) {
2586       SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown);
2587     }
2588   }
2589   else {
2590     if (color_blend != NULL) {
2591       widget_state_blend(wt->wcol.inner, color_blend, wcol_state->blend);
2592     }
2593
2594     if (state & UI_ACTIVE) { /* mouse over? */
2595       widget_active_color(wt->wcol.inner);
2596     }
2597   }
2598
2599   if (state & UI_BUT_REDALERT) {
2600     uchar red[4] = {255, 0, 0};
2601     if (wt->draw) {
2602       widget_state_blend(wt->wcol.inner, red, 0.4f);
2603     }
2604     else {
2605       widget_state_blend(wt->wcol.text, red, 0.4f);
2606     }
2607   }
2608
2609   if (state & UI_BUT_DRAG_MULTI) {
2610     /* the button isn't SELECT but we're editing this so draw with sel color */
2611     copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel);
2612     SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown);
2613     widget_state_blend(wt->wcol.text, wt->wcol.text_sel, 0.85f);
2614   }
2615
2616   if (state & UI_BUT_NODE_ACTIVE) {
2617     uchar blue[4] = {86, 128, 194};
2618     widget_state_blend(wt->wcol.inner, blue, 0.3f);
2619   }
2620 }
2621
2622 /* sliders use special hack which sets 'item' as inner when drawing filling */
2623 static void widget_state_numslider(uiWidgetType *wt, int state, int drawflag)
2624 {
2625   uiWidgetStateColors *wcol_state = wt->wcol_state;
2626
2627   /* call this for option button */
2628   widget_state(wt, state, drawflag);
2629
2630   const uchar *color_blend = widget_color_blend_from_flags(wcol_state, state, drawflag);
2631   if (color_blend != NULL) {
2632     /* Set the slider 'item' so that it reflects state settings too.
2633      * De-saturate so the color of the slider doesn't conflict with the blend color,
2634      * which can make the color hard to see when the slider is set to full (see T66102). */
2635     wt->wcol.item[0] = wt->wcol.item[1] = wt->wcol.item[2] = rgb_to_grayscale_byte(wt->wcol.item);
2636     widget_state_blend(wt->wcol.item, color_blend, wcol_state->blend);
2637     rgb_ensure_contrast(wt->wcol.item, wt->wcol.inner, 30);
2638   }
2639
2640   if (state & UI_SELECT) {
2641     SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown);
2642   }
2643 }
2644
2645 /* labels use theme colors for text */
2646 static void widget_state_option_menu(uiWidgetType *wt, int state, int drawflag)
2647 {
2648   bTheme *btheme = UI_GetTheme(); /* XXX */
2649
2650   /* call this for option button */
2651   widget_state(wt, state, drawflag);
2652
2653   /* if not selected we get theme from menu back */
2654   if (state & UI_SELECT) {
2655     copy_v3_v3_uchar(wt->wcol.text, btheme->tui.wcol_menu_back.text_sel);
2656   }
2657   else {
2658     copy_v3_v3_uchar(wt->wcol.text, btheme->tui.wcol_menu_back.text);
2659   }
2660 }
2661
2662 static void widget_state_nothing(uiWidgetType *wt, int UNUSED(state), int UNUSED(drawflag))
2663 {
2664   wt->wcol = *(wt->wcol_theme);
2665 }
2666
2667 /* special case, button that calls pulldown */
2668 static void widget_state_pulldown(uiWidgetType *wt, int UNUSED(state), int UNUSED(drawflag))
2669 {
2670   wt->wcol = *(wt->wcol_theme);
2671 }
2672
2673 /* special case, pie menu items */
2674 static void widget_state_pie_menu_item(uiWidgetType *wt, int state, int UNUSED(drawflag))
2675 {
2676   wt->wcol = *(wt->wcol_theme);
2677
2678   /* active and disabled (not so common) */
2679   if ((state & UI_BUT_DISABLED) && (state & UI_ACTIVE)) {
2680     widget_state_blend(wt->wcol.text, wt->wcol.text_sel, 0.5f);
2681     /* draw the backdrop at low alpha, helps navigating with keys
2682      * when disabled items are active */
2683     copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.item);
2684     wt->wcol.inner[3] = 64;
2685   }
2686   else {
2687     /* regular active */
2688     if (state & (UI_SELECT | UI_ACTIVE)) {
2689       copy_v3_v3_uchar(wt->wcol.text, wt->wcol.text_sel);
2690     }
2691     else if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) {
2692       /* regular disabled */
2693       widget_state_blend(wt->wcol.text, wt->wcol.inner, 0.5f);
2694     }
2695
2696     if (state & UI_SELECT) {
2697       copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel);
2698     }
2699     else if (state & UI_ACTIVE) {
2700       copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.item);
2701     }
2702   }
2703 }
2704
2705 /* special case, menu items */
2706 static void widget_state_menu_item(uiWidgetType *wt, int state, int UNUSED(drawflag))
2707 {
2708   wt->wcol = *(wt->wcol_theme);
2709
2710   /* active and disabled (not so common) */
2711   if ((state & UI_BUT_DISABLED) && (state & UI_ACTIVE)) {
2712     /* draw the backdrop at low alpha, helps navigating with keys
2713      * when disabled items are active */
2714     wt->wcol.text[3] = 128;
2715     widget_state_blend(wt->wcol.inner, wt->wcol.text, 0.5f);
2716     wt->wcol.inner[3] = 64;
2717   }
2718   else {
2719     /* regular active */
2720     if (state & UI_ACTIVE) {
2721       copy_v3_v3_uchar(wt->wcol.text, wt->wcol.text_sel);
2722     }
2723     else if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) {
2724       /* regular disabled */
2725       widget_state_blend(wt->wcol.text, wt->wcol.inner, 0.5f);
2726     }
2727
2728     if (state & UI_ACTIVE) {
2729       copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel);
2730     }
2731   }
2732 }
2733
2734 /* ************ menu backdrop ************************* */
2735
2736 /* outside of rect, rad to left/bottom/right */
2737 static void widget_softshadow(const rcti *rect, int roundboxalign, const float radin)
2738 {
2739   bTheme *btheme = UI_GetTheme();
2740   uiWidgetBase wtb;
2741   rcti rect1 = *rect;
2742   float alphastep;
2743   int step, totvert;
2744   float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2];
2745   const float radout = UI_ThemeMenuShadowWidth();
2746
2747   /* disabled shadow */
2748   if (radout == 0.0f) {
2749     return;
2750   }
2751
2752   /* prevent tooltips to not show round shadow */
2753   if (radout > 0.2f * BLI_rcti_size_y(&rect1)) {
2754     rect1.ymax -= 0.2f * BLI_rcti_size_y(&rect1);
2755   }
2756   else {
2757     rect1.ymax -= radout;
2758   }
2759
2760   /* inner part */
2761   totvert = round_box_shadow_edges(wtb.inner_v,
2762                                    &rect1,
2763                                    radin,
2764                                    roundboxalign & (UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT),
2765                                    0.0f);
2766
2767   /* we draw a number of increasing size alpha quad strips */
2768   alphastep = 3.0f * btheme->tui.menu_shadow_fac / radout;
2769
2770   uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
2771
2772   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2773
2774   for (step = 1; step <= (int)radout; step++) {
2775     float expfac = sqrtf(step / radout);
2776
2777     round_box_shadow_edges(wtb.outer_v, &rect1, radin, UI_CNR_ALL, (float)step);
2778
2779     immUniformColor4f(0.0f, 0.0f, 0.0f, alphastep * (1.0f - expfac));
2780
2781     widget_verts_to_triangle_strip(&wtb, totvert, triangle_strip);
2782
2783     widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_STRIP, triangle_strip, NULL, totvert * 2);
2784   }
2785
2786   immUnbindProgram();
2787 }
2788
2789 static void widget_menu_back(uiWidgetColors *wcol, rcti *rect, int flag, int direction)
2790 {
2791   uiWidgetBase wtb;
2792   int roundboxalign = UI_CNR_ALL;
2793
2794   widget_init(&wtb);
2795
2796   /* menu is 2nd level or deeper */
2797   if (flag & UI_BLOCK_POPUP) {
2798     // rect->ymin -= 4.0;
2799     // rect->ymax += 4.0;
2800   }
2801   else if (direction == UI_DIR_DOWN) {
2802     roundboxalign = (UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT);
2803     rect->ymin -= 0.1f * U.widget_unit;
2804   }
2805   else if (direction == UI_DIR_UP) {
2806     roundboxalign = UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT;
2807     rect->ymax += 0.1f * U.widget_unit;
2808   }
2809
2810   GPU_blend_set_func_separate(
2811       GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
2812   GPU_blend(true);
2813   widget_softshadow(rect, roundboxalign, wcol->roundness * U.widget_unit);
2814
2815   round_box_edges(&wtb, roundboxalign, rect, wcol->roundness * U.widget_unit);
2816   wtb.draw_emboss = false;
2817   widgetbase_draw(&wtb, wcol);
2818
2819   GPU_blend(false);
2820 }
2821
2822 static void ui_hsv_cursor(float x, float y)
2823 {
2824   uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
2825
2826   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2827
2828   immUniformColor3f(1.0f, 1.0f, 1.0f);
2829   imm_draw_circle_fill_2d(pos, x, y, 3.0f * U.pixelsize, 8);
2830
2831   GPU_blend(true);
2832   GPU_line_smooth(true);
2833   immUniformColor3f(0.0f, 0.0f, 0.0f);
2834   imm_draw_circle_wire_2d(pos, x, y, 3.0f * U.pixelsize, 12);
2835   GPU_blend(false);
2836   GPU_line_smooth(false);
2837
2838   immUnbindProgram();
2839 }
2840
2841 void ui_hsvcircle_vals_from_pos(
2842     const rcti *rect, const float mx, const float my, float *r_val_rad, float *r_val_dist)
2843 {
2844   /* duplication of code... well, simple is better now */
2845   const float centx = BLI_rcti_cent_x_fl(rect);
2846   const float centy = BLI_rcti_cent_y_fl(rect);
2847   const float radius = (float)min_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)) / 2.0f;
2848   const float m_delta[2] = {mx - centx, my - centy};
2849   const float dist_sq = len_squared_v2(m_delta);
2850
2851   *r_val_dist = (dist_sq < (radius * radius)) ? sqrtf(dist_sq) / radius : 1.0f;
2852   *r_val_rad = atan2f(m_delta[0], m_delta[1]) / (2.0f * (float)M_PI) + 0.5f;
2853 }
2854
2855 /* cursor in hsv circle, in float units -1 to 1, to map on radius */
2856 void ui_hsvcircle_pos_from_vals(
2857     const ColorPicker *cpicker, const rcti *rect, const float *hsv, float *r_xpos, float *r_ypos)
2858 {
2859   /* duplication of code... well, simple is better now */
2860   const float centx = BLI_rcti_cent_x_fl(rect);
2861   const float centy = BLI_rcti_cent_y_fl(rect);
2862   float radius = (float)min_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)) / 2.0f;
2863   float ang, radius_t;
2864
2865   ang = 2.0f * (float)M_PI * hsv[0] + (float)M_PI_2;
2866
2867   if (cpicker->use_color_cubic && (U.color_picker_type == USER_CP_CIRCLE_HSV)) {
2868     radius_t = (1.0f - pow3f(1.0f - hsv[1]));
2869   }
2870   else {
2871     radius_t = hsv[1];
2872   }
2873
2874   radius = clamp_f(radius_t, 0.0f, 1.0f) * radius;
2875   *r_xpos = centx + cosf(-ang) * radius;
2876   *r_ypos = centy + sinf(-ang) * radius;
2877 }
2878
2879 static void ui_draw_but_HSVCIRCLE(uiBut *but, const uiWidgetColors *wcol, const rcti *rect)
2880 {
2881   /* TODO(merwin): reimplement as shader for pixel-perfect colors */
2882
2883   const int tot = 64;
2884   const float radstep = 2.0f * (float)M_PI / (float)tot;
2885   const float centx = BLI_rcti_cent_x_fl(rect);
2886   const float centy = BLI_rcti_cent_y_fl(rect);
2887   float radius = (float)min_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)) / 2.0f;
2888
2889   ColorPicker *cpicker = but->custom_data;
2890   float rgb[3], hsv[3], rgb_center[3];
2891   bool is_color_gamma = ui_but_is_color_gamma(but);
2892
2893   /* Initialize for compatibility. */
2894   copy_v3_v3(hsv, cpicker->color_data);
2895
2896   /* Compute current hue. */
2897   ui_but_v3_get(but, rgb);
2898   ui_scene_linear_to_color_picker_space(but, rgb);
2899   ui_rgb_to_color_picker_compat_v(rgb, hsv);
2900
2901   CLAMP(hsv[2], 0.0f, 1.0f); /* for display only */
2902
2903   /* exception: if 'lock' is set
2904    * lock the value of the color wheel to 1.
2905    * Useful for color correction tools where you're only interested in hue. */
2906   if (cpicker->use_color_lock) {
2907     if (U.color_picker_type == USER_CP_CIRCLE_HSV) {
2908       hsv[2] = 1.0f;
2909     }
2910     else {
2911       hsv[2] = 0.5f;
2912     }
2913   }
2914
2915   const float hsv_center[3] = {0.0f, 0.0f, hsv[2]};
2916   ui_color_picker_to_rgb_v(hsv_center, rgb_center);
2917   ui_color_picker_to_scene_linear_space(but, rgb_center);
2918
2919   if (!is_color_gamma) {
2920     ui_block_cm_to_display_space_v3(but->block, rgb_center);
2921   }
2922
2923   GPUVertFormat *format = immVertexFormat();
2924   uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
2925   uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
2926
2927   immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
2928
2929   immBegin(GPU_PRIM_TRI_FAN, tot + 2);
2930   immAttr3fv(color, rgb_center);
2931   immVertex2f(pos, centx, centy);
2932
2933   float ang = 0.0f;
2934   for (int a = 0; a <= tot; a++, ang += radstep) {
2935     float si = sinf(ang);
2936     float co = cosf(ang);
2937     float hsv_ang[3];
2938     float rgb_ang[3];
2939
2940     ui_hsvcircle_vals_from_pos(
2941         rect, centx + co * radius, centy + si * radius, hsv_ang, hsv_ang + 1);
2942     hsv_ang[2] = hsv[2];
2943
2944     ui_color_picker_to_rgb_v(hsv_ang, rgb_ang);
2945     ui_color_picker_to_scene_linear_space(but, rgb_ang);
2946
2947     if (!is_color_gamma) {
2948       ui_block_cm_to_display_space_v3(but->block, rgb_ang);
2949     }
2950
2951     immAttr3fv(color, rgb_ang);
2952     immVertex2f(pos, centx + co * radius, centy + si * radius);
2953   }
2954   immEnd();
2955   immUnbindProgram();
2956
2957   /* fully rounded outline */
2958   format = immVertexFormat();
2959   pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
2960
2961   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2962
2963   GPU_blend(true);
2964   GPU_line_smooth(true);
2965
2966   immUniformColor3ubv(wcol->outline);
2967   imm_draw_circle_wire_2d(pos, centx, centy, radius, tot);
2968
2969   immUnbindProgram();
2970
2971   GPU_blend(false);
2972   GPU_line_smooth(false);
2973
2974   /* cursor */
2975   copy_v3_v3(hsv, cpicker->color_data);
2976   ui_but_v3_get(but, rgb);
2977   ui_scene_linear_to_color_picker_space(but, rgb);
2978   ui_rgb_to_color_picker_compat_v(rgb, hsv);
2979
2980   float xpos, ypos;
2981   ui_hsvcircle_pos_from_vals(cpicker, rect, hsv, &xpos, &ypos);
2982   ui_hsv_cursor(xpos, ypos);
2983 }
2984
2985 /* ************ custom buttons, old stuff ************** */
2986
2987 /* draws in resolution of 48x4 colors */
2988 void ui_draw_gradient(const rcti *rect, const float hsv[3], const int type, const float alpha)
2989 {
2990   /* allows for 4 steps (red->yellow) */
2991   const int steps = 48;
2992   const float color_step = 1.0f / steps;
2993   int a;
2994   float h = hsv[0], s = hsv[1], v = hsv[2];
2995   float dx, dy, sx1, sx2, sy;
2996   float col0[4][3]; /* left half, rect bottom to top */
2997   float col1[4][3]; /* right half, rect bottom to top */
2998
2999   /* draw series of gouraud rects */
3000
3001   switch (type) {
3002     case UI_GRAD_SV:
3003       hsv_to_rgb(h, 0.0, 0.0, &col1[0][0], &col1[0][1], &col1[0][2]);
3004       hsv_to_rgb(h, 0.0, 0.333, &col1[1][0], &col1[1][1], &col1[1][2]);
3005       hsv_to_rgb(h, 0.0, 0.666, &col1[2][0], &col1[2][1], &col1[2][2]);
3006       hsv_to_rgb(h, 0.0, 1.0, &col1[3][0], &col1[3][1], &col1[3][2]);
3007       break;
3008     case UI_GRAD_HV:
3009       hsv_to_rgb(0.0, s, 0.0, &col1[0][0], &col1[0][1], &col1[0][2]);
3010       hsv_to_rgb(0.0, s, 0.333, &col1[1][0], &col1[1][1], &col1[1][2]);
3011       hsv_to_rgb(0.0, s, 0.666, &col1[2][0], &col1[2][1], &col1[2][2]);
3012       hsv_to_rgb(0.0, s, 1.0, &col1[3][0], &col1[3][1], &col1[3][2]);
3013       break;
3014     case UI_GRAD_HS:
3015       hsv_to_rgb(0.0, 0.0, v, &col1[0][0], &col1[0][1], &col1[0][2]);
3016       hsv_to_rgb(0.0, 0.333, v, &col1[1][0], &col1[1][1], &col1[1][2]);
3017       hsv_to_rgb(0.0, 0.666, v, &col1[2][0], &col1[2][1], &col1[2][2]);
3018       hsv_to_rgb(0.0, 1.0, v, &col1[3][0], &col1[3][1], &col1[3][2]);
3019       break;
3020     case UI_GRAD_H:
3021       hsv_to_rgb(0.0, 1.0, 1.0, &col1[0][0], &col1[0][1], &col1[0][2]);
3022       copy_v3_v3(col1[1], col1[0]);
3023       copy_v3_v3(col1[2], col1[0]);
3024       copy_v3_v3(col1[3], col1[0]);
3025       break;
3026     case UI_GRAD_S:
3027       hsv_to_rgb(1.0, 0.0, 1.0, &col1[1][0], &col1[1][1], &col1[1][2]);
3028       copy_v3_v3(col1[0], col1[1]);
3029       copy_v3_v3(col1[2], col1[1]);
3030       copy_v3_v3(col1[3], col1[1]);
3031       break;
3032     case UI_GRAD_V:
3033       hsv_to_rgb(1.0, 1.0, 0.0, &col1[2][0], &col1[2][1], &col1[2][2]);
3034       copy_v3_v3(col1[0], col1[2]);
3035       copy_v3_v3(col1[1], col1[2]);
3036       copy_v3_v3(col1[3], col1[2]);
3037       break;
3038     default:
3039       assert(!"invalid 'type' argument");
3040       hsv_to_rgb(1.0, 1.0, 1.0, &col1[2][0], &col1[2][1], &col1[2][2]);
3041       copy_v3_v3(col1[0], col1[2]);
3042       copy_v3_v3(col1[1], col1[2]);
3043       copy_v3_v3(col1[3], col1[2]);
3044       break;
3045   }
3046
3047   /* old below */
3048   GPUVertFormat *format = immVertexFormat();
3049   uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
3050   uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
3051   immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
3052
3053   immBegin(GPU_PRIM_TRIS, steps * 3 * 6);
3054
3055   /* 0.999 = prevent float inaccuracy for steps */
3056   for (dx = 0.0f; dx < 0.999f; dx += color_step) {
3057     const float dx_next = dx + color_step;
3058
3059     /* previous color */
3060     copy_v3_v3(col0[0], col1[0]);
3061     copy_v3_v3(col0[1], col1[1]);
3062     copy_v3_v3(col0[2], col1[2]);
3063     copy_v3_v3(col0[3], col1[3]);
3064
3065     /* new color */
3066     switch (type) {
3067       case UI_GRAD_SV:
3068         hsv_to_rgb(h, dx, 0.0, &col1[0][0], &col1[0][1], &col1[0][2]);
3069         hsv_to_rgb(h, dx, 0.333, &col1[1][0], &col1[1][1], &col1[1][2]);
3070         hsv_to_rgb(h, dx, 0.666, &col1[2][0], &col1[2][1], &col1[2][2]);
3071         hsv_to_rgb(h, dx, 1.0, &col1[3][0], &col1[3][1], &col1[3][2]);
3072         break;
3073       case UI_GRAD_HV:
3074         hsv_to_rgb(dx_next, s, 0.0, &col1[0][0], &col1[0][1], &col1[0][2]);
3075         hsv_to_rgb(dx_next, s, 0.333, &col1[1][0], &col1[1][1], &col1[1][2]);
3076         hsv_to_rgb(dx_next, s, 0.666, &col1[2][0], &col1[2][1], &col1[2][2]);
3077         hsv_to_rgb(dx_next, s, 1.0, &col1[3][0], &col1[3][1], &col1[3][2]);
3078         break;
3079       case UI_GRAD_HS:
3080         hsv_to_rgb(dx_next, 0.0, v, &col1[0][0], &col1[0][1], &col1[0][2]);
3081         hsv_to_rgb(dx_next, 0.333, v, &col1[1][0], &col1[1][1], &col1[1][2]);
3082         hsv_to_rgb(dx_next, 0.666, v, &col1[2][0], &col1[2][1], &col1[2][2]);
3083         hsv_to_rgb(dx_next, 1.0, v, &col1[3][0], &col1[3][1], &col1[3][2]);
3084         break;
3085       case UI_GRAD_H:
3086         /* annoying but without this the color shifts - could be solved some other way
3087          * - campbell */
3088         hsv_to_rgb(dx_next, 1.0, 1.0, &col1[0][0], &col1[0][1], &col1[0][2]);
3089         copy_v3_v3(col1[1], col1[0]);
3090         copy_v3_v3(col1[2], col1[0]);
3091         copy_v3_v3(col1[3], col1[0]);
3092         break;
3093       case UI_GRAD_S:
3094         hsv_to_rgb(h, dx, 1.0, &col1[1][0], &col1[1][1], &col1[1][2]);
3095         copy_v3_v3(col1[0], col1[1]);
3096         copy_v3_v3(col1[2], col1[1]);
3097         copy_v3_v3(col1[3], col1[1]);
3098         break;
3099       case UI_GRAD_V:
3100         hsv_to_rgb(h, 1.0, dx, &col1[2][0], &col1[2][1], &col1[2][2]);
3101         copy_v3_v3(col1[0], col1[2]);
3102         copy_v3_v3(col1[1], col1[2]);
3103         copy_v3_v3(col1[3], col1[2]);
3104         break;
3105     }
3106
3107     /* rect */
3108     sx1 = rect->xmin + dx * BLI_rcti_size_x(rect);
3109     sx2 = rect->xmin + dx_next * BLI_rcti_size_x(rect);
3110     sy = rect->ymin;
3111     dy = (float)BLI_rcti_size_y(rect) / 3.0f;
3112
3113     for (a = 0; a < 3; a++, sy += dy) {
3114       immAttr4f(col, col0[a][0], col0[a][1], col0[a][2], alpha);
3115       immVertex2f(pos, sx1, sy);
3116
3117       immAttr4f(col, col1[a][0], col1[a][1], col1[a][2], alpha);
3118       immVertex2f(pos, sx2, sy);
3119
3120       immAttr4f(col, col1[a + 1][0], col1[a + 1][1], col1[a + 1][2], alpha);
3121       immVertex2f(pos, sx2, sy + dy);
3122
3123       immAttr4f(col, col0[a][0], col0[a][1], col0[a][2], alpha);
3124       immVertex2f(pos, sx1, sy);
3125
3126       immAttr4f(col, col1[a + 1][0], col1[a + 1][1], col1[a + 1][2], alpha);
3127       immVertex2f(pos, sx2, sy + dy);
3128
3129       immAttr4f(col, col0[a + 1][0], col0[a + 1][1], col0[a + 1][2], alpha);
3130       immVertex2f(pos, sx1, sy + dy);
3131     }
3132   }
3133   immEnd();
3134
3135   immUnbindProgram();
3136 }
3137
3138 void ui_hsvcube_pos_from_vals(
3139     const uiBut *but, const rcti *rect, const float *hsv, float *r_xp, float *r_yp)
3140 {
3141   float x = 0.0f, y = 0.0f;
3142
3143   switch ((int)but->a1) {
3144     case UI_GRAD_SV:
3145       x = hsv[1];
3146       y = hsv[2];
3147       break;
3148     case UI_GRAD_HV:
3149       x = hsv[0];
3150       y = hsv[2];
3151       break;
3152     case UI_GRAD_HS:
3153       x = hsv[0];
3154       y = hsv[1];
3155       break;
3156     case UI_GRAD_H:
3157       x = hsv[0];
3158       y = 0.5;
3159       break;
3160     case UI_GRAD_S:
3161       x = hsv[1];
3162       y = 0.5;
3163       break;
3164     case UI_GRAD_V:
3165       x = hsv[2];
3166       y = 0.5;
3167       break;
3168     case UI_GRAD_L_ALT:
3169       x = 0.5f;
3170       /* exception only for value strip - use the range set in but->min/max */
3171       y = hsv[2];
3172       break;
3173     case UI_GRAD_V_ALT:
3174       x = 0.5f;
3175       /* exception only for value strip - use the range set in but->min/max */
3176       y = (hsv[2] - but->softmin) / (but->softmax - but->softmin);
3177       break;
3178   }
3179
3180   /* cursor */
3181   *r_xp = rect->xmin + x * BLI_rcti_size_x(rect);
3182   *r_yp = rect->ymin + y * BLI_rcti_size_y(rect);
3183 }
3184
3185 static void ui_draw_but_HSVCUBE(uiBut *but, const rcti *rect)
3186 {
3187   float rgb[3];
3188   float x = 0.0f, y = 0.0f;
3189   ColorPicker *cpicker = but->custom_data;
3190   float *hsv = cpicker->color_data;
3191   float hsv_n[3];
3192
3193   /* Initialize for compatibility. */
3194   copy_v3_v3(hsv_n, hsv);
3195
3196   ui_but_v3_get(but, rgb);
3197   ui_scene_linear_to_color_picker_space(but, rgb);
3198   rgb_to_hsv_compat_v(rgb, hsv_n);
3199
3200   ui_draw_gradient(rect, hsv_n, but->a1, 1.0f);
3201
3202   ui_hsvcube_pos_from_vals(but, rect, hsv_n, &x, &y);
3203   CLAMP(x, rect->xmin + 3.0f, rect->xmax - 3.0f);
3204   CLAMP(y, rect->ymin + 3.0f, rect->ymax - 3.0f);
3205
3206   ui_hsv_cursor(x, y);
3207
3208   /* outline */
3209   uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
3210   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
3211   immUniformColor3ub(0, 0, 0);
3212   imm_draw_box_wire_2d(pos, (rect->xmin), (rect->ymin), (rect->xmax), (rect->ymax));
3213   immUnbindProgram();
3214 }
3215
3216 /* vertical 'value' slider, using new widget code */
3217 static void ui_draw_but_HSV_v(uiBut *but, const rcti *rect)
3218 {
3219   bTheme *btheme = UI_GetTheme();
3220   uiWidgetColors *wcol = &btheme->tui.wcol_numslider;
3221   uiWidgetBase wtb;
3222   const float rad = wcol->roundness * BLI_rcti_size_x(rect);
3223   float x, y;
3224   float rgb[3], hsv[3], v;
3225
3226   ui_but_v3_get(but, rgb);
3227   ui_scene_linear_to_color_picker_space(but, rgb);
3228
3229   if (but->a1 == UI_GRAD_L_ALT) {
3230     rgb_to_hsl_v(rgb, hsv);
3231   }
3232   else {
3233     rgb_to_hsv_v(rgb, hsv);
3234   }
3235   v = hsv[2];
3236
3237   /* map v from property range to [0,1] */
3238   if (but->a1 == UI_GRAD_V_ALT) {
3239     float min = but->softmin, max = but->softmax;
3240     v = (v - min) / (max - min);
3241   }
3242
3243   widget_init(&wtb);
3244
3245   /* fully rounded */
3246   round_box_edges(&wtb, UI_CNR_ALL, rect, rad);
3247
3248   /* setup temp colors */
3249   widgetbase_draw(&wtb,
3250                   &((uiWidgetColors){
3251                       .outline = {0, 0, 0, 255},
3252                       .inner = {128, 128, 128, 255},
3253                       .shadetop = 127,
3254                       .shadedown = -128,
3255                       .shaded = 1,
3256                   }));
3257
3258   /* We are drawing on top of widget bases. Flush cache. */
3259   GPU_blend(true);
3260   UI_widgetbase_draw_cache_flush();
3261   GPU_blend(false);
3262
3263   /* cursor */
3264   x = rect->xmin + 0.5f * BLI_rcti_size_x(rect);
3265   y = rect->ymin + v * BLI_rcti_size_y(rect);
3266   CLAMP(y, rect->ymin + 3.0f, rect->ymax - 3.0f);
3267
3268   ui_hsv_cursor(x, y);
3269 }
3270
3271 /* Generic round-box drawing. */
3272 static void ui_draw_roundbox(const rcti *rect, const float rad, const uiWidgetColors *wcol)
3273 {
3274   uiWidgetBase wtb;
3275   widget_init(&wtb);
3276   round_box_edges(&wtb, UI_CNR_ALL, rect, rad);
3277   widgetbase_draw(&wtb, wcol);
3278
3279   /* We are drawing on top of widget bases. Flush cache. */
3280   GPU_blend(true);
3281   UI_widgetbase_draw_cache_flush();
3282   GPU_blend(false);
3283 }
3284
3285 /* ************ separator, for menus etc ***************** */
3286 static void ui_draw_separator(const rcti *rect, const uiWidgetColors *wcol)
3287 {
3288   int y = rect->ymin + BLI_rcti_size_y(rect) / 2 - 1;
3289   uchar col[4] = {
3290       wcol->text[0],
3291       wcol->text[1],
3292       wcol->text[2],
3293       30,
3294   };
3295
3296   uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
3297   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
3298
3299   GPU_blend(true);
3300   immUniformColor4ubv(col);
3301   GPU_line_width(1.0f);
3302
3303   immBegin(GPU_PRIM_LINES, 2);
3304   immVertex2f(pos, rect->xmin, y);
3305   immVertex2f(pos, rect->xmax, y);
3306   immEnd();
3307
3308   GPU_blend(false);
3309
3310   immUnbindProgram();
3311 }
3312
3313 /* ************ button callbacks, draw ***************** */
3314 static void widget_numbut_draw(
3315     uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, bool emboss)
3316 {
3317   uiWidgetBase wtb;
3318   const float rad = wcol->roundness * BLI_rcti_size_y(rect);
3319   const int handle_width = min_ii(BLI_rcti_size_x(rect) / 3, BLI_rcti_size_y(rect) * 0.7f);
3320
3321   if (state & UI_SELECT) {
3322     SWAP(short, wcol->shadetop, wcol->shadedown);
3323   }
3324
3325   widget_init(&wtb);
3326
3327   if (!emboss) {
3328     round_box_edges(&wtb, roundboxalign, rect, rad);
3329   }
3330   else {
3331     wtb.draw_inner = false;
3332     wtb.draw_outline = false;
3333   }
3334
3335   /* decoration */
3336   if ((state & UI_ACTIVE) && !(state & UI_STATE_TEXT_INPUT)) {
3337     uiWidgetColors wcol_zone;
3338     uiWidgetBase wtb_zone;
3339     rcti rect_zone;
3340     int roundboxalign_zone;
3341
3342     /* left arrow zone */
3343     widget_init(&wtb_zone);
3344     wtb_zone.draw_outline = false;
3345     wtb_zone.draw_emboss = false;
3346
3347     wcol_zone = *wcol;
3348     copy_v3_v3_uchar(wcol_zone.item, wcol->text);
3349     if (state & UI_STATE_ACTIVE_LEFT) {
3350       widget_active_color(wcol_zone.inner);
3351     }
3352
3353     rect_zone = *rect;
3354     rect_zone.xmax = rect->xmin + handle_width + U.pixelsize;
3355     roundboxalign_zone = roundboxalign & ~(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT);
3356     round_box_edges(&wtb_zone, roundboxalign_zone, &rect_zone, rad);
3357
3358     shape_preset_init_number_arrows(&wtb_zone.tria1, &rect_zone, 0.6f, 'l');
3359     widgetbase_draw(&wtb_zone, &wcol_zone);
3360
3361     /* right arrow zone */
3362     widget_init(&wtb_zone);
3363     wtb_zone.draw_outline = false;
3364     wtb_zone.draw_emboss = false;
3365     wtb_zone.tria1.type = ROUNDBOX_TRIA_ARROWS;
3366
3367     wcol_zone = *wcol;
3368     copy_v3_v3_uchar(wcol_zone.item, wcol->text);
3369     if (state & UI_STATE_ACTIVE_RIGHT) {
3370       widget_active_color(wcol_zone.inner);
3371     }
3372
3373     rect_zone = *rect;
3374     rect_zone.xmin = rect->xmax - handle_width - U.pixelsize;
3375     roundboxalign_zone = roundboxalign & ~(UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT);
3376     round_box_edges(&wtb_zone, roundboxalign_zone, &rect_zone, rad);
3377
3378     shape_preset_init_number_arrows(&wtb_zone.tria2, &rect_zone, 0.6f, 'r');
3379     widgetbase_draw(&wtb_zone, &wcol_zone);
3380
3381     /* middle highlight zone */
3382     widget_init(&wtb_zone);
3383     wtb_zone.draw_outline = false;
3384     wtb_zone.draw_emboss = false;
3385
3386     wcol_zone = *wcol;
3387     copy_v3_v3_uchar(wcol_zone.item, wcol->text);
3388     if (!(state & (UI_STATE_ACTIVE_LEFT | UI_STATE_ACTIVE_RIGHT))) {
3389       widget_active_color(wcol_zone.inner);
3390     }
3391
3392     rect_zone = *rect;
3393     rect_zone.xmin = rect->xmin + handle_width - U.pixelsize;
3394     rect_zone.xmax = rect->xmax - handle_width + U.pixelsize;
3395     round_box_edges(&wtb_zone, 0, &rect_zone, 0);
3396     widgetbase_draw(&wtb_zone, &wcol_zone);
3397
3398     /* outline */
3399     wtb.draw_inner = false;
3400     widgetbase_draw(&wtb, wcol);
3401   }
3402   else {
3403     /* inner and outline */
3404     widgetbase_draw(&wtb, wcol);
3405   }
3406
3407   if (!(state & UI_STATE_TEXT_INPUT)) {
3408     const float textofs = 0.425f * BLI_rcti_size_y(rect);
3409
3410     /* text space */
3411     rect->xmin += textofs;
3412     rect->xmax -= textofs;
3413   }
3414 }
3415
3416 static void widget_numbut(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
3417 {
3418   widget_numbut_draw(wcol, rect, state, roundboxalign, false);
3419 }
3420
3421 /**
3422  * Draw number buttons still with triangles when field is not embossed
3423  */
3424 static void widget_numbut_embossn(
3425     uiBut *UNUSED(but), uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
3426 {
3427   widget_numbut_draw(wcol, rect, state, roundboxalign, true);
3428 }
3429
3430 /* function in use for buttons and for view2d sliders */
3431 void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *slider, int state)
3432 {
3433   uiWidgetBase wtb;
3434   int horizontal;
3435   float rad;
3436   bool outline = false;
3437
3438   widget_init(&wtb);
3439
3440   /* determine horizontal/vertical */
3441   horizontal = (BLI_rcti_size_x(rect) > BLI_rcti_size_y(rect));
3442
3443   if (horizontal) {
3444     rad = wcol->roundness * BLI_rcti_size_y(rect);
3445   }
3446   else {
3447     rad = wcol->roundness * BLI_rcti_size_x(rect);
3448   }
3449
3450   wtb.uniform_params.shade_dir = (horizontal) ? 1.0f : 0.0;
3451
3452   /* draw back part, colors swapped and shading inverted */
3453   if (horizontal) {
3454     SWAP(short, wcol->shadetop, wcol->shadedown);
3455   }
3456
3457   round_box_edges(&wtb, UI_CNR_ALL, rect, rad);
3458   widgetbase_draw(&wtb, wcol);
3459
3460   /* slider */
3461   if ((BLI_rcti_size_x(slider) < 2) || (BLI_rcti_size_y(slider) < 2)) {
3462     /* pass */
3463   }
3464   else {
3465     SWAP(short, wcol->shadetop, wcol->shadedown);
3466
3467     copy_v4_v4_uchar(wcol->inner, wcol->item);
3468
3469     if (wcol->shadetop > wcol->shadedown) {
3470       wcol->shadetop += 20; /* XXX violates themes... */
3471     }
3472     else {
3473       wcol->shadedown += 20;
3474     }
3475
3476     if (state & UI_SCROLL_PRESSED) {
3477       wcol->inner[0] = wcol->inner[0] >= 250 ? 255 : wcol->inner[0] + 5;
3478       wcol->inner[1] = wcol->inner[1] >= 250 ? 255 : wcol->inner[1] + 5;
3479       wcol->inner[2] = wcol->inner[2] >= 250 ? 255 : wcol->inner[2] + 5;
3480     }
3481
3482     /* draw */
3483     wtb.draw_emboss = false; /* only emboss once */
3484
3485     /* exception for progress bar */
3486     if (state & UI_SCROLL_NO_OUTLINE) {
3487       SWAP(bool, outline, wtb.draw_outline);
3488     }
3489
3490     round_box_edges(&wtb, UI_CNR_ALL, slider, rad);
3491
3492     if (state & UI_SCROLL_ARROWS) {
3493       if (wcol->item[0] > 48) {
3494         wcol->item[0] -= 48;
3495       }
3496       if (wcol->item[1] > 48) {
3497         wcol->item[1] -= 48;
3498       }
3499       if (wcol->item[2] > 48) {
3500         wcol->item[2] -= 48;
3501       }
3502       wcol->item[3] = 255;
3503
3504       if (horizontal) {
3505         rcti slider_inset = *slider;
3506         slider_inset.xmin += 0.05 * U.widget_unit;
3507         slider_inset.xmax -= 0.05 * U.widget_unit;
3508         shape_preset_init_scroll_circle(&wtb.tria1, &slider_inset, 0.6f, 'l');
3509         shape_preset_init_scroll_circle(&wtb.tria2, &slider_inset, 0.6f, 'r');
3510       }
3511       else {
3512         shape_preset_init_scroll_circle(&wtb.tria1, slider, 0.6f, 'b');
3513         shape_preset_init_scroll_circle(&wtb.tria2, slider, 0.6f, 't');
3514       }
3515     }
3516     widgetbase_draw(&wtb, wcol);
3517
3518     if (state & UI_SCROLL_NO_OUTLINE) {
3519       SWAP(bool, outline, wtb.draw_outline);
3520     }
3521   }
3522 }
3523
3524 static void widget_scroll(
3525     uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int UNUSED(roundboxalign))
3526 {
3527   rcti rect1;
3528   double value;
3529   float fac, size, min;
3530   int horizontal;
3531
3532   /* calculate slider part */
3533   value = ui_but_value_get(but);
3534
3535   size = (but->softmax + but->a1 - but->softmin);
3536   size = max_ff(size, 2.0f);
3537
3538   /* position */
3539   rect1 = *rect;
3540
3541   /* determine horizontal/vertical */
3542   horizontal = (BLI_rcti_size_x(rect) > BLI_rcti_size_y(rect));
3543
3544   if (horizontal) {
3545     fac = BLI_rcti_size_x(rect) / size;
3546     rect1.xmin = rect1.xmin + ceilf(fac * ((float)value - but->softmin));
3547     rect1.xmax = rect1.xmin + ceilf(fac * (but->a1 - but->softmin));
3548
3549     /* ensure minimium size */
3550     min = BLI_rcti_size_y(rect);
3551
3552     if (BLI_rcti_size_x(&rect1) < min) {
3553       rect1.xmax = rect1.xmin + min;
3554
3555       if (rect1.xmax > rect->xmax) {
3556         rect1.xmax = rect->xmax;
3557         rect1.xmin = max_ii(rect1.xmax - min, rect->xmin);
3558       }
3559     }
3560   }
3561   else {
3562     fac = BLI_rcti_size_y(rect) / size;
3563     rect1.ymax = rect1.ymax - ceilf(fac * ((float)value - but->softmin));
3564     rect1.ymin = rect1.ymax - ceilf(fac * (but->a1 - but->softmin));
3565
3566     /* ensure minimium size */
3567     min = BLI_rcti_size_x(rect);
3568
3569     if (BLI_rcti_size_y(&rect1) < min) {
3570       rect1.ymax = rect1.ymin + min;
3571
3572       if (rect1.ymax > rect->ymax) {
3573         rect1.ymax = rect->ymax;
3574         rect1.ymin = max_ii(rect1.ymax - min, rect->ymin);
3575       }
3576     }
3577   }
3578
3579   if (state & UI_SELECT) {
3580     state = UI_SCROLL_PRESSED;
3581   }
3582   else {
3583     state = 0;
3584   }
3585   UI_draw_widget_scroll(wcol, rect, &rect1, state);
3586 }
3587
3588 static void widget_progressbar(
3589     uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign)
3590 {
3591   uiWidgetBase wtb, wtb_bar;
3592   rcti rect_prog = *rect, rect_bar = *rect;
3593
3594   widget_init(&wtb);
3595   widget_init(&wtb_bar);
3596
3597   /* round corners */
3598   float value = but->a1;
3599   float offs = wcol->roundness * BLI_rcti_size_y(&rect_prog);
3600   float w = value * BLI_rcti_size_x(&rect_prog);
3601
3602   /* ensure minimium size */
3603   w = MAX2(w, offs);
3604
3605   rect_bar.xmax = rect_bar.xmin + w;
3606
3607   round_box_edges(&wtb, roundboxalign, &rect_prog, offs);
3608   round_box_edges(&wtb_bar, roundboxalign, &rect_bar, offs);
3609
3610   wtb.draw_outline = true;
3611   widgetbase_draw(&wtb, wcol);
3612
3613   /* "slider" bar color */
3614   copy_v3_v3_uchar(wcol->inner, wcol->item);
3615   widgetbase_draw(&wtb_bar, wcol);
3616
3617   /* raise text a bit */
3618   rect->xmin += (BLI_rcti_size_x(&rect_prog) / 2);
3619   rect->xmax += (BLI_rcti_size_x(&rect_prog) / 2);
3620 }
3621
3622 static void widget_nodesocket(
3623     uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign))
3624 {
3625   uiWidgetBase wtb;
3626   int radi = 5;
3627   uchar old_inner[3], old_outline[3];
3628
3629   widget_init(&wtb);
3630
3631   copy_v3_v3_uchar(old_inner, wcol->inner);
3632   copy_v3_v3_uchar(old_outline, wcol->outline);
3633
3634   wcol->inner[0] = but->col[0];
3635   wcol->inner[1] = but->col[1];
3636   wcol->inner[2] = but->col[2];
3637   wcol->outline[0] = 0;
3638   wcol->outline[1] = 0;
3639   wcol->outline[2] = 0;
3640   wcol->outline[3] = 150;
3641
3642   int cent_x = BLI_rcti_cent_x(rect);
3643   int cent_y = BLI_rcti_cent_y(rect);
3644   rect->xmin = cent_x - radi;
3645   rect->xmax = cent_x + radi;
3646   rect->ymin = cent_y - radi;
3647   rect->ymax = cent_y + radi;
3648
3649   wtb.draw_outline = true;
3650   round_box_edges(&wtb, UI_CNR_ALL, rect, (float)radi);
3651   widgetbase_draw(&wtb, wcol);
3652
3653   copy_v3_v3_uchar(wcol->inner, old_inner);
3654   copy_v3_v3_uchar(wcol->outline, old_outline);
3655 }
3656
3657 static void widget_numslider(
3658     uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
3659 {
3660   uiWidgetBase wtb, wtb1;
3661   rcti rect1;
3662   float offs, toffs;
3663   uchar outline[3];
3664
3665   widget_init(&wtb);
3666   widget_init(&wtb1);
3667
3668   /* Backdrop first. */
3669   offs = wcol->roundness * BLI_rcti_size_y(rect);
3670   toffs = offs * 0.75f;
3671   round_box_edges(&wtb, roundboxalign, rect, offs);
3672
3673   wtb.draw_outline = false;
3674   widgetbase_draw(&wtb, wcol);
3675
3676   /* Draw slider part only when not in text editing. */
3677   if (!(state & UI_STATE_TEXT_INPUT)) {
3678     int roundboxalign_slider = roundboxalign;
3679
3680     copy_v3_v3_uchar(outline, wcol->outline);
3681     copy_v3_v3_uchar(wcol->outline, wcol->item);
3682     copy_v3_v3_uchar(wcol->inner, wcol->item);
3683
3684     if (!(state & UI_SELECT)) {
3685       SWAP(short, wcol->shadetop, wcol->shadedown);
3686     }
3687
3688     rect1 = *rect;
3689     float factor, factor_ui;
3690     float factor_discard = 1.0f; /* No discard. */
3691     float value = (float)ui_but_value_get(but);
3692
3693     if (but->rnaprop && (RNA_property_subtype(but->rnaprop) == PROP_PERCENTAGE)) {
3694       factor = value / but->softmax;
3695     }
3696     else {
3697       factor = (value - but->softmin) / (but->softmax - but->softmin);
3698     }
3699
3700     float width = (float)BLI_rcti_size_x(rect);
3701     factor_ui = factor * width;
3702
3703     if (factor_ui <= offs) {
3704       /* Left part only. */
3705       roundboxalign_slider &= ~(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT);
3706       rect1.xmax = rect1.xmin + offs;
3707       factor_discard = factor_ui / offs;
3708     }
3709     else if (factor_ui <= width - offs) {
3710       /* Left part + middle part. */
3711       roundboxalign_slider &= ~(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT);
3712       rect1.xmax = rect1.xmin + factor_ui;
3713     }
3714     else {
3715       /* Left part + middle part + right part. */
3716       factor_discard = factor;
3717     }
3718
3719     round_box_edges(&wtb1, roundboxalign_slider, &rect1, offs);
3720     wtb1.draw_outline = false;
3721     widgetbase_set_uniform_discard_factor(&wtb1, factor_discard);
3722     widgetbase_draw(&wtb1, wcol);
3723
3724     copy_v3_v3_uchar(wcol->outline, outline);
3725
3726     if (!(state & UI_SELECT)) {
3727       SWAP(short, wcol->shadetop, wcol->shadedown);
3728     }
3729   }
3730
3731   /* Outline. */
3732   wtb.draw_outline = true;
3733   wtb.draw_inner = false;
3734   widgetbase_draw(&wtb, wcol);
3735
3736   /* Add space at either side of the button so text aligns with numbuttons
3737    * (which have arrow icons). */
3738   if (!(state & UI_STATE_TEXT_INPUT)) {
3739     rect->xmax -= toffs;
3740     rect->xmin += toffs;
3741   }
3742 }
3743
3744 /* I think 3 is sufficient border to indicate keyed status */
3745 #define SWATCH_KEYED_BORDER 3
3746
3747 static void widget_swatch(
3748     uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
3749 {
3750   uiWidgetBase wtb;
3751   float rad, col[4];
3752
3753   col[3] = 1.0f;
3754
3755   if (but->rnaprop) {
3756     BLI_assert(but->rnaindex == -1);
3757
3758     if (RNA_property_array_length(&but->rnapoin, but->rnaprop) == 4) {
3759       col[3] = RNA_property_float_get_index(&but->rnapoin, but->rnaprop, 3);
3760     }
3761   }
3762
3763   widget_init(&wtb);
3764
3765   rad = wcol->roundness * U.widget_unit;
3766   round_box_edges(&wtb, roundboxalign, rect, rad);
3767
3768   ui_but_v3_get(but, col);
3769
3770   if ((state & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_OVERRIDEN |
3771                 UI_BUT_REDALERT)) ||
3772       (but->drawflag & UI_BUT_ANIMATED_CHANGED)) {
3773     /* draw based on state - color for keyed etc */
3774     widgetbase_draw(&wtb, wcol);
3775
3776     /* inset to draw swatch color */
3777     rect->xmin += SWATCH_KEYED_BORDER;
3778     rect->xmax -= SWATCH_KEYED_BORDER;
3779     rect->ymin += SWATCH_KEYED_BORDER;
3780     rect->ymax -= SWATCH_KEYED_BORDER;
3781
3782     round_box_edges(&wtb, roundboxalign, rect, rad);
3783   }
3784
3785   if (!ui_but_is_color_gamma(but)) {
3786     ui_block_cm_to_display_space_v3(but->block, col);
3787   }
3788
3789   rgba_float_to_uchar(wcol->inner, col);
3790   const bool show_alpha_checkers = (wcol->inner[3] < 255);
3791
3792   wcol->shaded = 0;
3793
3794   if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) {
3795     /* Now we reduce alpha of the inner color (i.e. the color shown)
3796      * so that this setting can look grayed out, while retaining
3797      * the checkerboard (for transparent values). This is needed
3798      * here as the effects of ui_widget_color_disabled() are overwritten.
3799      */
3800     wcol->inner[3] /= 2;
3801   }
3802
3803   widgetbase_draw_ex(&wtb, wcol, show_alpha_checkers);
3804   if (but->a1 == UI_PALETTE_COLOR &&
3805       ((Palette *)but->rnapoin.owner_id)->active_color == (int)but->a2) {
3806     float width = rect->xmax - rect->xmin;
3807     float height = rect->ymax - rect->ymin;
3808     /* find color luminance and change it slightly */
3809     float bw = rgb_to_grayscale(col);
3810
3811     bw += (bw < 0.5f) ? 0.5f : -0.5f;
3812
3813     /* We are drawing on top of widget bases. Flush cache. */
3814     GPU_blend(true);
3815     UI_widgetbase_draw_cache_flush();
3816     GPU_blend(false);
3817
3818     uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
3819     immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
3820
3821     immUniformColor3f(bw, bw, bw);
3822     immBegin(GPU_PRIM_TRIS, 3);
3823     immVertex2f(pos, rect->xmin + 0.1f * width, rect->ymin + 0.9f * height);
3824     immVertex2f(pos, rect->xmin + 0.1f * width, rect->ymin + 0.5f * height);
3825     immVertex2f(pos, rect->xmin + 0.5f * width, rect->ymin + 0.9f * height);
3826     immEnd();
3827
3828     immUnbindProgram();
3829   }
3830 }
3831
3832 static void widget_unitvec(
3833     uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign))
3834 {
3835   ui_draw_but_UNITVEC(but, wcol, rect);
3836 }
3837
3838 static void widget_icon_has_anim(
3839     uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
3840 {
3841   if (state & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_REDALERT)) {
3842     uiWidgetBase wtb;
3843     float rad;
3844
3845     widget_init(&wtb);
3846     wtb.draw_outline = false;
3847
3848     rad = wcol->roundness * BLI_rcti_size_y(rect);
3849     round_box_edges(&wtb, UI_CNR_ALL, rect, rad);
3850     widgetbase_draw(&wtb, wcol);
3851   }
3852   else if (but->type == UI_BTYPE_NUM) {
3853     /* Draw number buttons still with left/right
3854      * triangles when field is not embossed */
3855     widget_numbut_embossn(but, wcol, rect, state, roundboxalign);
3856   }
3857 }
3858
3859 static void widget_textbut(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
3860 {
3861   uiWidgetBase wtb;
3862   float rad;
3863
3864   if (state & UI_SELECT) {
3865     SWAP(short, wcol->shadetop, wcol->shadedown);
3866   }
3867
3868   widget_init(&wtb);
3869
3870   rad = wcol->roundness * U.widget_unit;
3871   round_box_edges(&wtb, roundboxalign, rect, rad);
3872
3873   widgetbase_draw(&wtb, wcol);
3874 }
3875
3876 static void widget_menubut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign)
3877 {
3878   uiWidgetBase wtb;
3879   float rad;
3880
3881   widget_init(&wtb);
3882
3883   rad = wcol->roundness * U.widget_unit;
3884   round_box_edges(&wtb, roundboxalign, rect, rad);
3885
3886   /* decoration */
3887   shape_preset_trias_from_rect_menu(&wtb.tria1, rect);
3888   /* copy size and center to 2nd tria */
3889   wtb.tria2 = wtb.tria1;
3890
3891   widgetbase_draw(&wtb, wcol);
3892
3893   /* text space, arrows are about 0.6 height of button */
3894   rect->xmax -= (6 * BLI_rcti_size_y(rect)) / 10;
3895 }
3896
3897 static void widget_menuiconbut(uiWidgetColors *wcol,
3898                                rcti *rect,
3899                                int UNUSED(state),
3900                                int roundboxalign)
3901 {
3902   uiWidgetBase wtb;
3903   float rad;
3904
3905   widget_init(&wtb);
3906
3907   rad = wcol->roundness * U.widget_unit;
3908   round_box_edges(&wtb, roundboxalign, rect, rad);
3909
3910   /* decoration */
3911   widgetbase_draw(&wtb, wcol);
3912 }
3913
3914 static void widget_pulldownbut(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
3915 {
3916   float back[4];
3917   UI_GetThemeColor4fv(TH_BACK, back);
3918
3919   if ((state & UI_ACTIVE) || (back[3] < 1.0f)) {
3920     uiWidgetBase wtb;
3921     const float rad = wcol->roundness * U.widget_unit;
3922
3923     if (state & UI_ACTIVE) {
3924       copy_v4_v4_uchar(wcol->inner, wcol->inner_sel);
3925       copy_v3_v3_uchar(wcol->text, wcol->text_sel);
3926       copy_v3_v3_uchar(wcol->outline, wcol->inner);
3927     }
3928     else {
3929       wcol->inner[3] *= 1.0f - back[3];
3930       wcol->outline[3] = 0.0f;
3931     }
3932
3933     widget_init(&wtb);
3934
3935     /* half rounded */
3936     round_box_edges(&wtb, roundboxalign, rect, rad);
3937
3938     widgetbase_draw(&wtb, wcol);
3939   }
3940 }
3941
3942 static void widget_menu_itembut(uiWidgetColors *wcol,
3943                                 rcti *rect,
3944                                 int UNUSED(state),
3945                                 int UNUSED(roundboxalign))
3946 {
3947   uiWidgetBase wtb;
3948
3949   widget_init(&wtb);
3950
3951   /* not rounded, no outline */
3952   wtb.draw_outline = false;
3953   round_box_edges(&wtb, 0, rect, 0.0f);
3954
3955   widgetbase_draw(&wtb, wcol);
3956 }
3957
3958 static void widget_menu_radial_itembut(
3959     uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign))
3960 {
3961   uiWidgetBase wtb;
3962   float rad;
3963   float fac = but->block->pie_data.alphafac;
3964
3965   widget_init(&wtb);
3966
3967   wtb.draw_emboss = false;
3968
3969   rad = wcol->roundness * BLI_rcti_size_y(rect);
3970   round_box_edges(&wtb, UI_CNR_ALL, rect, rad);
3971
3972   wcol->inner[3] *= fac;
3973   wcol->inner_sel[3] *= fac;
3974   wcol->item[3] *= fac;
3975   wcol->text[3] *= fac;
3976   wcol->text_sel[3] *= fac;
3977   wcol->outline[3] *= fac;
3978
3979   widgetbase_draw(&wtb, wcol);
3980 }
3981
3982 static void widget_list_itembut(uiWidgetColors *wcol,
3983                                 rcti *rect,
3984                                 int UNUSED(state),
3985                                 int UNUSED(roundboxalign))
3986 {
3987   uiWidgetBase wtb;
3988   float rad;
3989
3990   widget_init(&wtb);
3991
3992   /* no outline */
3993   wtb.draw_outline = false;
3994   rad = wcol->roundness * U.widget_unit;
3995   round_box_edges(&wtb, UI_CNR_ALL, rect, rad);
3996
3997   widgetbase_draw(&wtb, wcol);
3998 }
3999
4000 static void widget_optionbut(uiWidgetColors *wcol,
4001                              rcti *rect,
4002                              int state,
4003                              int UNUSED(roundboxalign))
4004 {
4005   bool text_before_widget = (state & UI_STATE_TEXT_BEFORE_WIDGET);
4006   uiWidgetBase wtb;
4007   rcti recttemp = *rect;
4008   float rad;
4009   int delta;
4010
4011   widget_init(&wtb);
4012
4013   /* square */
4014   if (text_before_widget) {
4015     recttemp.xmin = recttemp.xmax - BLI_rcti_size_y(&recttemp);
4016   }
4017   else {
4018     recttemp.xmax = recttemp.xmin + BLI_rcti_size_y(&recttemp);
4019   }
4020
4021   /* smaller */
4022   delta = 1 + BLI_rcti_size_y(&recttemp) / 8;
4023   recttemp.xmin += delta;
4024   recttemp.ymin += delta;
4025   recttemp.xmax -= delta;
4026   recttemp.ymax -= delta;
4027
4028   rad = wcol->roundness * BLI_rcti_size_y(&recttemp);
4029   round_box_edges(&wtb, UI_CNR_ALL, &recttemp, rad);
4030
4031   /* decoration */
4032   if (state & UI_SELECT) {
4033     shape_preset_trias_from_rect_checkmark(&wtb.tria1, &recttemp);
4034   }
4035
4036   widgetbase_draw(&wtb, wcol);
4037
4038   /* text space */
4039   const float offset = BLI_rcti_size_y(rect) * 0.7 + delta;
4040   if (text_before_widget) {
4041     rect->xmax -= offset;
4042   }
4043   else {
4044     rect->xmin += offset;
4045   }
4046 }
4047
4048 /* labels use Editor theme colors for text */
4049 static void widget_state_label(uiWidgetType *wt, int state, int drawflag)
4050 {
4051   if (state & UI_BUT_LIST_ITEM) {
4052     /* Override default label theme's colors. */
4053     bTheme *btheme = UI_GetTheme();
4054     wt->wcol_theme = &btheme->tui.wcol_list_item;
4055     /* call this for option button */
4056     widget_state(wt, state, drawflag);
4057   }
4058   else {
4059     /* call this for option button */
4060     widget_state(wt, state, drawflag);
4061     if (state & UI_SELECT) {
4062       UI_GetThemeColor3ubv(TH_TEXT_HI, wt->wcol.text);
4063     }
4064     else {
4065       UI_GetThemeColor3ubv(TH_TEXT, wt->wcol.text);
4066     }
4067   }
4068
4069   if (state & UI_BUT_REDALERT) {
4070     uchar red[4] = {255, 0, 0};
4071     widget_state_blend(wt->wcol.text, red, 0.4f);
4072   }
4073 }
4074
4075 static void widget_radiobut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign)
4076 {
4077   uiWidgetBase wtb;
4078   float rad;
4079
4080   widget_init(&wtb);
4081
4082   rad = wcol->roundness * U.widget_unit;
4083   round_box_edges(&wtb, roundboxalign, rect, rad);
4084
4085   widgetbase_draw(&wtb, wcol);
4086 }
4087