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