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