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