1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2009 Blender Foundation.
17  * All rights reserved.
18  */
19 
20 /** \file
21  * \ingroup edinterface
22  */
23 
24 #include <limits.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include "DNA_brush_types.h"
29 #include "DNA_userdef_types.h"
30 
31 #include "BLI_listbase.h"
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 "ED_node.h"
45 
46 #include "UI_interface.h"
47 #include "UI_interface_icons.h"
48 
49 #include "interface_intern.h"
50 
51 #include "GPU_batch.h"
52 #include "GPU_batch_presets.h"
53 #include "GPU_immediate.h"
54 #include "GPU_immediate_util.h"
55 #include "GPU_matrix.h"
56 #include "GPU_platform.h"
57 #include "GPU_state.h"
58 
59 #ifdef WITH_INPUT_IME
60 #  include "WM_types.h"
61 #endif
62 
63 /* -------------------------------------------------------------------- */
64 /** \name Local Enums/Defines
65  * \{ */
66 
67 /* icons are 80% of height of button (16 pixels inside 20 height) */
68 #define ICON_SIZE_FROM_BUTRECT(rect) (0.8f * BLI_rcti_size_y(rect))
69 
70 /* visual types for drawing */
71 /* for time being separated from functional types */
72 typedef enum {
73   /* default */
74   UI_WTYPE_REGULAR,
75 
76   /* standard set */
77   UI_WTYPE_LABEL,
78   UI_WTYPE_TOGGLE,
79   UI_WTYPE_CHECKBOX,
80   UI_WTYPE_RADIO,
81   UI_WTYPE_NUMBER,
82   UI_WTYPE_SLIDER,
83   UI_WTYPE_EXEC,
84   UI_WTYPE_TOOLBAR_ITEM,
85   UI_WTYPE_TAB,
86   UI_WTYPE_TOOLTIP,
87 
88   /* strings */
89   UI_WTYPE_NAME,
90   UI_WTYPE_NAME_LINK,
91   UI_WTYPE_POINTER_LINK,
92   UI_WTYPE_FILENAME,
93 
94   /* menus */
95   UI_WTYPE_MENU_RADIO,
96   UI_WTYPE_MENU_ICON_RADIO,
97   UI_WTYPE_MENU_POINTER_LINK,
98   UI_WTYPE_MENU_NODE_LINK,
99 
100   UI_WTYPE_PULLDOWN,
101   UI_WTYPE_MENU_ITEM,
102   UI_WTYPE_MENU_ITEM_RADIAL,
103   UI_WTYPE_MENU_BACK,
104 
105   /* specials */
106   UI_WTYPE_ICON,
107   UI_WTYPE_ICON_LABEL,
108   UI_WTYPE_SWATCH,
109   UI_WTYPE_RGB_PICKER,
110   UI_WTYPE_UNITVEC,
111   UI_WTYPE_BOX,
112   UI_WTYPE_SCROLL,
113   UI_WTYPE_LISTITEM,
114   UI_WTYPE_PROGRESSBAR,
115   UI_WTYPE_NODESOCKET,
116 } uiWidgetTypeEnum;
117 
118 /* Button state argument shares bits with 'uiBut.flag'.
119  * reuse flags that aren't needed for drawing to avoid collision. */
120 enum {
121   /* Show that holding the button opens a menu. */
122   UI_STATE_HOLD_ACTION = UI_BUT_UPDATE_DELAY,
123   UI_STATE_TEXT_INPUT = UI_BUT_UNDO,
124   UI_STATE_ACTIVE_LEFT = UI_BUT_VALUE_CLEAR,
125   UI_STATE_ACTIVE_RIGHT = UI_BUT_TEXTEDIT_UPDATE,
126   UI_STATE_TEXT_BEFORE_WIDGET = UI_BUT_IMMEDIATE,
127 
128   UI_STATE_FLAGS_ALL = (UI_STATE_HOLD_ACTION | UI_STATE_TEXT_INPUT | UI_STATE_ACTIVE_LEFT |
129                         UI_STATE_ACTIVE_RIGHT | UI_STATE_TEXT_BEFORE_WIDGET),
130 };
131 /* Prevent accidental use. */
132 #define UI_BUT_UPDATE_DELAY ((void)0)
133 #define UI_BUT_UNDO ((void)0)
134 
135 /** \} */
136 
137 /* -------------------------------------------------------------------- */
138 /** \name Internal Color Utilities
139  * \{ */
140 
color_blend_v3_v3(uchar cp[3],const uchar cpstate[3],const float fac)141 static void color_blend_v3_v3(uchar cp[3], const uchar cpstate[3], const float fac)
142 {
143   if (fac != 0.0f) {
144     cp[0] = (int)((1.0f - fac) * cp[0] + fac * cpstate[0]);
145     cp[1] = (int)((1.0f - fac) * cp[1] + fac * cpstate[1]);
146     cp[2] = (int)((1.0f - fac) * cp[2] + fac * cpstate[2]);
147   }
148 }
149 
color_blend_v4_v4v4(uchar r_col[4],const uchar col1[4],const uchar col2[4],const float fac)150 static void color_blend_v4_v4v4(uchar r_col[4],
151                                 const uchar col1[4],
152                                 const uchar col2[4],
153                                 const float fac)
154 {
155   const int faci = unit_float_to_uchar_clamp(fac);
156   const int facm = 255 - faci;
157 
158   r_col[0] = (faci * col1[0] + facm * col2[0]) / 256;
159   r_col[1] = (faci * col1[1] + facm * col2[1]) / 256;
160   r_col[2] = (faci * col1[2] + facm * col2[2]) / 256;
161   r_col[3] = (faci * col1[3] + facm * col2[3]) / 256;
162 }
163 
color_add_v3_i(uchar cp[3],int tint)164 static void color_add_v3_i(uchar cp[3], int tint)
165 {
166   cp[0] = clamp_i(cp[0] + tint, 0, 255);
167   cp[1] = clamp_i(cp[1] + tint, 0, 255);
168   cp[2] = clamp_i(cp[2] + tint, 0, 255);
169 }
170 
color_ensure_contrast_v3(uchar cp[3],const uchar cp_other[3],int contrast)171 static void color_ensure_contrast_v3(uchar cp[3], const uchar cp_other[3], int contrast)
172 {
173   BLI_assert(contrast > 0);
174   const int item_value = rgb_to_grayscale_byte(cp);
175   const int inner_value = rgb_to_grayscale_byte(cp_other);
176   const int delta = item_value - inner_value;
177   if (delta >= 0) {
178     if (contrast > delta) {
179       color_add_v3_i(cp, contrast - delta);
180     }
181   }
182   else {
183     if (contrast > -delta) {
184       color_add_v3_i(cp, -contrast - delta);
185     }
186   }
187 }
188 
color_mul_hsl_v3(uchar ch[3],float h_factor,float s_factor,float l_factor)189 static void color_mul_hsl_v3(uchar ch[3], float h_factor, float s_factor, float l_factor)
190 {
191   float rgb[3], hsl[3];
192   rgb_uchar_to_float(rgb, ch);
193   rgb_to_hsl_v(rgb, hsl);
194   hsl[0] *= h_factor;
195   hsl[1] *= s_factor;
196   hsl[2] *= l_factor;
197   hsl_to_rgb_v(hsl, rgb);
198   rgb_float_to_uchar(ch, rgb);
199 }
200 
201 /** \} */
202 
203 /* -------------------------------------------------------------------- */
204 /** \name Widget Base Type
205  * \{ */
206 
207 /**
208  * - in: roundbox codes for corner types and radius
209  * - return: array of `[size][2][x, y]` points, the edges of the roundbox, + UV coords
210  *
211  * - draw black box with alpha 0 on exact button boundbox
212  * - for every AA step:
213  *    - draw the inner part for a round filled box, with color blend codes or texture coords
214  *    - draw outline in outline color
215  *    - draw outer part, bottom half, extruded 1 pixel to bottom, for emboss shadow
216  *    - draw extra decorations
217  * - draw background color box with alpha 1 on exact button boundbox
218  */
219 
220 /* fill this struct with polygon info to draw AA'ed */
221 /* it has outline, back, and two optional tria meshes */
222 
223 typedef struct uiWidgetTrias {
224   uint tot;
225   int type;
226   float size, center[2];
227 
228   float vec[16][2];
229   const uint (*index)[3];
230 
231 } uiWidgetTrias;
232 
233 /* max as used by round_box__edges */
234 /* Make sure to change widget_base_vert.glsl accordingly. */
235 #define WIDGET_CURVE_RESOLU 9
236 #define WIDGET_SIZE_MAX (WIDGET_CURVE_RESOLU * 4)
237 
238 typedef struct uiWidgetBase {
239   /* TODO remove these completely */
240   int totvert, halfwayvert;
241   float outer_v[WIDGET_SIZE_MAX][2];
242   float inner_v[WIDGET_SIZE_MAX][2];
243   float inner_uv[WIDGET_SIZE_MAX][2];
244 
245   bool draw_inner, draw_outline, draw_emboss;
246 
247   uiWidgetTrias tria1;
248   uiWidgetTrias tria2;
249 
250   /* Widget shader parameters, must match the shader layout. */
251   uiWidgetBaseParameters uniform_params;
252 } uiWidgetBase;
253 
254 /**
255  * For time being only for visual appearance,
256  * later, a handling callback can be added too.
257  */
258 typedef struct uiWidgetType {
259 
260   /* pointer to theme color definition */
261   const uiWidgetColors *wcol_theme;
262   uiWidgetStateColors *wcol_state;
263 
264   /* converted colors for state */
265   uiWidgetColors wcol;
266 
267   void (*state)(struct uiWidgetType *, int state, int drawflag);
268   void (*draw)(uiWidgetColors *, rcti *, int state, int roundboxalign);
269   void (*custom)(uiBut *, uiWidgetColors *, rcti *, int state, int roundboxalign);
270   void (*text)(const uiFontStyle *, const uiWidgetColors *, uiBut *, rcti *);
271 
272 } uiWidgetType;
273 
274 /** \} */
275 
276 /* -------------------------------------------------------------------- */
277 /** \name Shape Preset Data
278  * \{ */
279 
280 static const float cornervec[WIDGET_CURVE_RESOLU][2] = {
281     {0.0, 0.0},
282     {0.195, 0.02},
283     {0.383, 0.067},
284     {0.55, 0.169},
285     {0.707, 0.293},
286     {0.831, 0.45},
287     {0.924, 0.617},
288     {0.98, 0.805},
289     {1.0, 1.0},
290 };
291 
292 const float ui_pixel_jitter[UI_PIXEL_AA_JITTER][2] = {
293     {0.468813, -0.481430},
294     {-0.155755, -0.352820},
295     {0.219306, -0.238501},
296     {-0.393286, -0.110949},
297     {-0.024699, 0.013908},
298     {0.343805, 0.147431},
299     {-0.272855, 0.269918},
300     {0.095909, 0.388710},
301 };
302 #define WIDGET_AA_JITTER UI_PIXEL_AA_JITTER
303 #define jit ui_pixel_jitter
304 
305 static const float g_shape_preset_number_arrow_vert[3][2] = {
306     {-0.352077, 0.532607},
307     {-0.352077, -0.549313},
308     {0.330000, -0.008353},
309 };
310 static const uint g_shape_preset_number_arrow_face[1][3] = {
311     {0, 1, 2},
312 };
313 
314 static const float g_shape_preset_scroll_circle_vert[16][2] = {
315     {0.382684, 0.923879},
316     {0.000001, 1.000000},
317     {-0.382683, 0.923880},
318     {-0.707107, 0.707107},
319     {-0.923879, 0.382684},
320     {-1.000000, 0.000000},
321     {-0.923880, -0.382684},
322     {-0.707107, -0.707107},
323     {-0.382683, -0.923880},
324     {0.000000, -1.000000},
325     {0.382684, -0.923880},
326     {0.707107, -0.707107},
327     {0.923880, -0.382684},
328     {1.000000, -0.000000},
329     {0.923880, 0.382683},
330     {0.707107, 0.707107},
331 };
332 static const uint g_shape_preset_scroll_circle_face[14][3] = {
333     {0, 1, 2},
334     {2, 0, 3},
335     {3, 0, 15},
336     {3, 15, 4},
337     {4, 15, 14},
338     {4, 14, 5},
339     {5, 14, 13},
340     {5, 13, 6},
341     {6, 13, 12},
342     {6, 12, 7},
343     {7, 12, 11},
344     {7, 11, 8},
345     {8, 11, 10},
346     {8, 10, 9},
347 };
348 
349 static const float g_shape_preset_menu_arrow_vert[6][2] = {
350     {-0.33, 0.16},
351     {0.33, 0.16},
352     {0, 0.82},
353     {0, -0.82},
354     {-0.33, -0.16},
355     {0.33, -0.16},
356 };
357 static const uint g_shape_preset_menu_arrow_face[2][3] = {{2, 0, 1}, {3, 5, 4}};
358 
359 static const float g_shape_preset_checkmark_vert[6][2] = {
360     {-0.578579, 0.253369},
361     {-0.392773, 0.412794},
362     {-0.004241, -0.328551},
363     {-0.003001, 0.034320},
364     {1.055313, 0.864744},
365     {0.866408, 1.026895},
366 };
367 
368 static const uint g_shape_preset_checkmark_face[4][3] = {
369     {3, 2, 4},
370     {3, 4, 5},
371     {1, 0, 3},
372     {0, 2, 3},
373 };
374 
375 #define OY (-0.2 / 2)
376 #define SC (0.35 * 2)
377 static const float g_shape_preset_hold_action_vert[6][2] = {
378     {-0.5 + SC, 1.0 + OY},
379     {0.5, 1.0 + OY},
380     {0.5, 0.0 + OY + SC},
381 };
382 static const uint g_shape_preset_hold_action_face[2][3] = {{2, 0, 1}, {3, 5, 4}};
383 #undef OY
384 #undef SC
385 
386 /** \} */
387 
388 /* -------------------------------------------------------------------- */
389 /** \name #GPUBatch Creation
390  *
391  * In order to speed up UI drawing we create some batches that are then
392  * modified by specialized shaders to draw certain elements really fast.
393  * TODO: find a better place. Maybe its own file?
394  *
395  * \{ */
396 
397 static struct {
398   GPUBatch *roundbox_widget;
399   GPUBatch *roundbox_shadow;
400 
401   /* TODO remove */
402   GPUVertFormat format;
403   uint vflag_id;
404 } g_ui_batch_cache = {0};
405 
vflag_format(void)406 static GPUVertFormat *vflag_format(void)
407 {
408   if (g_ui_batch_cache.format.attr_len == 0) {
409     GPUVertFormat *format = &g_ui_batch_cache.format;
410     g_ui_batch_cache.vflag_id = GPU_vertformat_attr_add(
411         format, "vflag", GPU_COMP_U32, 1, GPU_FETCH_INT);
412   }
413   return &g_ui_batch_cache.format;
414 }
415 
416 #define INNER 0
417 #define OUTLINE 1
418 #define EMBOSS 2
419 #define NO_AA 0
420 
set_roundbox_vertex_data(GPUVertBufRaw * vflag_step,uint32_t d)421 static void set_roundbox_vertex_data(GPUVertBufRaw *vflag_step, uint32_t d)
422 {
423   uint32_t *data = GPU_vertbuf_raw_step(vflag_step);
424   *data = d;
425 }
426 
set_roundbox_vertex(GPUVertBufRaw * vflag_step,int corner_id,int corner_v,int jit_v,bool inner,bool emboss,int color)427 static uint32_t set_roundbox_vertex(GPUVertBufRaw *vflag_step,
428                                     int corner_id,
429                                     int corner_v,
430                                     int jit_v,
431                                     bool inner,
432                                     bool emboss,
433                                     int color)
434 {
435   uint32_t *data = GPU_vertbuf_raw_step(vflag_step);
436   *data = corner_id;
437   *data |= corner_v << 2;
438   *data |= jit_v << 6;
439   *data |= color << 12;
440   *data |= (inner) ? (1 << 10) : 0;  /* is inner vert */
441   *data |= (emboss) ? (1 << 11) : 0; /* is emboss vert */
442   return *data;
443 }
444 
ui_batch_roundbox_widget_get(void)445 GPUBatch *ui_batch_roundbox_widget_get(void)
446 {
447   if (g_ui_batch_cache.roundbox_widget == NULL) {
448     GPUVertBuf *vbo = GPU_vertbuf_create_with_format(vflag_format());
449 
450     GPU_vertbuf_data_alloc(vbo, 12);
451 
452     GPUIndexBufBuilder ibuf;
453     GPU_indexbuf_init(&ibuf, GPU_PRIM_TRIS, 6, 12);
454     /* Widget */
455     GPU_indexbuf_add_tri_verts(&ibuf, 0, 1, 2);
456     GPU_indexbuf_add_tri_verts(&ibuf, 2, 1, 3);
457     /* Trias */
458     GPU_indexbuf_add_tri_verts(&ibuf, 4, 5, 6);
459     GPU_indexbuf_add_tri_verts(&ibuf, 6, 5, 7);
460 
461     GPU_indexbuf_add_tri_verts(&ibuf, 8, 9, 10);
462     GPU_indexbuf_add_tri_verts(&ibuf, 10, 9, 11);
463 
464     g_ui_batch_cache.roundbox_widget = GPU_batch_create_ex(
465         GPU_PRIM_TRIS, vbo, GPU_indexbuf_build(&ibuf), GPU_BATCH_OWNS_INDEX | GPU_BATCH_OWNS_VBO);
466     gpu_batch_presets_register(g_ui_batch_cache.roundbox_widget);
467   }
468   return g_ui_batch_cache.roundbox_widget;
469 }
470 
ui_batch_roundbox_shadow_get(void)471 GPUBatch *ui_batch_roundbox_shadow_get(void)
472 {
473   if (g_ui_batch_cache.roundbox_shadow == NULL) {
474     uint32_t last_data;
475     GPUVertBufRaw vflag_step;
476     GPUVertBuf *vbo = GPU_vertbuf_create_with_format(vflag_format());
477     const int vcount = (WIDGET_SIZE_MAX + 1) * 2 + 2 + WIDGET_SIZE_MAX;
478     GPU_vertbuf_data_alloc(vbo, vcount);
479     GPU_vertbuf_attr_get_raw_data(vbo, g_ui_batch_cache.vflag_id, &vflag_step);
480 
481     for (int c = 0; c < 4; c++) {
482       for (int a = 0; a < WIDGET_CURVE_RESOLU; a++) {
483         set_roundbox_vertex(&vflag_step, c, a, NO_AA, true, false, INNER);
484         set_roundbox_vertex(&vflag_step, c, a, NO_AA, false, false, INNER);
485       }
486     }
487     /* close loop */
488     last_data = set_roundbox_vertex(&vflag_step, 0, 0, NO_AA, true, false, INNER);
489     last_data = set_roundbox_vertex(&vflag_step, 0, 0, NO_AA, false, false, INNER);
490     /* restart */
491     set_roundbox_vertex_data(&vflag_step, last_data);
492     set_roundbox_vertex(&vflag_step, 0, 0, NO_AA, true, false, INNER);
493     /* filled */
494     for (int c1 = 0, c2 = 3; c1 < 2; c1++, c2--) {
495       for (int a1 = 0, a2 = WIDGET_CURVE_RESOLU - 1; a2 >= 0; a1++, a2--) {
496         set_roundbox_vertex(&vflag_step, c1, a1, NO_AA, true, false, INNER);
497         set_roundbox_vertex(&vflag_step, c2, a2, NO_AA, true, false, INNER);
498       }
499     }
500     g_ui_batch_cache.roundbox_shadow = GPU_batch_create_ex(
501         GPU_PRIM_TRI_STRIP, vbo, NULL, GPU_BATCH_OWNS_VBO);
502     gpu_batch_presets_register(g_ui_batch_cache.roundbox_shadow);
503   }
504   return g_ui_batch_cache.roundbox_shadow;
505 }
506 
507 #undef INNER
508 #undef OUTLINE
509 #undef EMBOSS
510 #undef NO_AA
511 
512 /** \} */
513 
514 /* -------------------------------------------------------------------- */
515 /** \name Draw Triangle Arrow
516  * \{ */
517 
UI_draw_anti_tria(float x1,float y1,float x2,float y2,float x3,float y3,const float color[4])518 void UI_draw_anti_tria(
519     float x1, float y1, float x2, float y2, float x3, float y3, const float color[4])
520 {
521   const float tri_arr[3][2] = {{x1, y1}, {x2, y2}, {x3, y3}};
522   float draw_color[4];
523 
524   copy_v4_v4(draw_color, color);
525   /* Note: This won't give back the original color. */
526   draw_color[3] *= 1.0f / WIDGET_AA_JITTER;
527 
528   GPU_blend(GPU_BLEND_ALPHA);
529 
530   const uint pos = GPU_vertformat_attr_add(
531       immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
532   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
533 
534   immUniformColor4fv(draw_color);
535   immBegin(GPU_PRIM_TRIS, 3 * WIDGET_AA_JITTER);
536 
537   /* for each AA step */
538   for (int j = 0; j < WIDGET_AA_JITTER; j++) {
539     immVertex2f(pos, tri_arr[0][0] + jit[j][0], tri_arr[0][1] + jit[j][1]);
540     immVertex2f(pos, tri_arr[1][0] + jit[j][0], tri_arr[1][1] + jit[j][1]);
541     immVertex2f(pos, tri_arr[2][0] + jit[j][0], tri_arr[2][1] + jit[j][1]);
542   }
543 
544   immEnd();
545 
546   immUnbindProgram();
547 
548   GPU_blend(GPU_BLEND_NONE);
549 }
550 
551 /* triangle 'icon' inside rect */
ui_draw_anti_tria_rect(const rctf * rect,char dir,const float color[4])552 void ui_draw_anti_tria_rect(const rctf *rect, char dir, const float color[4])
553 {
554   if (dir == 'h') {
555     const float half = 0.5f * BLI_rctf_size_y(rect);
556     UI_draw_anti_tria(
557         rect->xmin, rect->ymin, rect->xmin, rect->ymax, rect->xmax, rect->ymin + half, color);
558   }
559   else {
560     const float half = 0.5f * BLI_rctf_size_x(rect);
561     UI_draw_anti_tria(
562         rect->xmin, rect->ymax, rect->xmax, rect->ymax, rect->xmin + half, rect->ymin, color);
563   }
564 }
565 
UI_draw_anti_fan(float tri_array[][2],uint length,const float color[4])566 void UI_draw_anti_fan(float tri_array[][2], uint length, const float color[4])
567 {
568   float draw_color[4];
569 
570   copy_v4_v4(draw_color, color);
571   draw_color[3] *= 2.0f / WIDGET_AA_JITTER;
572 
573   GPU_blend(GPU_BLEND_ALPHA);
574 
575   const uint pos = GPU_vertformat_attr_add(
576       immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
577   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
578 
579   immUniformColor4fv(draw_color);
580 
581   /* for each AA step */
582   for (int j = 0; j < WIDGET_AA_JITTER; j++) {
583     immBegin(GPU_PRIM_TRI_FAN, length);
584     immVertex2f(pos, tri_array[0][0], tri_array[0][1]);
585     immVertex2f(pos, tri_array[1][0], tri_array[1][1]);
586 
587     /* We jitter only the middle of the fan, the extremes are pinned. */
588     for (int i = 2; i < length - 1; i++) {
589       immVertex2f(pos, tri_array[i][0] + jit[j][0], tri_array[i][1] + jit[j][1]);
590     }
591 
592     immVertex2f(pos, tri_array[length - 1][0], tri_array[length - 1][1]);
593     immEnd();
594   }
595 
596   immUnbindProgram();
597 
598   GPU_blend(GPU_BLEND_NONE);
599 }
600 
widget_init(uiWidgetBase * wtb)601 static void widget_init(uiWidgetBase *wtb)
602 {
603   wtb->totvert = wtb->halfwayvert = 0;
604   wtb->tria1.tot = 0;
605   wtb->tria2.tot = 0;
606   wtb->tria1.type = ROUNDBOX_TRIA_NONE;
607   wtb->tria1.size = 0;
608   wtb->tria2.size = 0;
609 
610   wtb->draw_inner = true;
611   wtb->draw_outline = true;
612   wtb->draw_emboss = true;
613 
614   wtb->uniform_params.shade_dir = 1.0f;
615   wtb->uniform_params.alpha_discard = 1.0f;
616 }
617 
618 /** \} */
619 
620 /* -------------------------------------------------------------------- */
621 /** \name Draw Round Box
622  * \{ */
623 
624 /* helper call, makes shadow rect, with 'sun' above menu, so only shadow to left/right/bottom */
625 /* return tot */
round_box_shadow_edges(float (* vert)[2],const rcti * rect,float rad,int roundboxalign,float step)626 static int round_box_shadow_edges(
627     float (*vert)[2], const rcti *rect, float rad, int roundboxalign, float step)
628 {
629   float vec[WIDGET_CURVE_RESOLU][2];
630   float minx, miny, maxx, maxy;
631   int a, tot = 0;
632 
633   rad += step;
634 
635   if (2.0f * rad > BLI_rcti_size_y(rect)) {
636     rad = 0.5f * BLI_rcti_size_y(rect);
637   }
638 
639   minx = rect->xmin - step;
640   miny = rect->ymin - step;
641   maxx = rect->xmax + step;
642   maxy = rect->ymax + step;
643 
644   /* mult */
645   for (a = 0; a < WIDGET_CURVE_RESOLU; a++) {
646     vec[a][0] = rad * cornervec[a][0];
647     vec[a][1] = rad * cornervec[a][1];
648   }
649 
650   /* start with left-top, anti clockwise */
651   if (roundboxalign & UI_CNR_TOP_LEFT) {
652     for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
653       vert[tot][0] = minx + rad - vec[a][0];
654       vert[tot][1] = maxy - vec[a][1];
655     }
656   }
657   else {
658     for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
659       vert[tot][0] = minx;
660       vert[tot][1] = maxy;
661     }
662   }
663 
664   if (roundboxalign & UI_CNR_BOTTOM_LEFT) {
665     for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
666       vert[tot][0] = minx + vec[a][1];
667       vert[tot][1] = miny + rad - vec[a][0];
668     }
669   }
670   else {
671     for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
672       vert[tot][0] = minx;
673       vert[tot][1] = miny;
674     }
675   }
676 
677   if (roundboxalign & UI_CNR_BOTTOM_RIGHT) {
678     for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
679       vert[tot][0] = maxx - rad + vec[a][0];
680       vert[tot][1] = miny + vec[a][1];
681     }
682   }
683   else {
684     for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
685       vert[tot][0] = maxx;
686       vert[tot][1] = miny;
687     }
688   }
689 
690   if (roundboxalign & UI_CNR_TOP_RIGHT) {
691     for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
692       vert[tot][0] = maxx - vec[a][1];
693       vert[tot][1] = maxy - rad + vec[a][0];
694     }
695   }
696   else {
697     for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
698       vert[tot][0] = maxx;
699       vert[tot][1] = maxy;
700     }
701   }
702   return tot;
703 }
704 
705 /* this call has 1 extra arg to allow mask outline */
round_box__edges(uiWidgetBase * wt,int roundboxalign,const rcti * rect,float rad,float radi)706 static void round_box__edges(
707     uiWidgetBase *wt, int roundboxalign, const rcti *rect, float rad, float radi)
708 {
709   float vec[WIDGET_CURVE_RESOLU][2], veci[WIDGET_CURVE_RESOLU][2];
710   const float minx = rect->xmin, miny = rect->ymin, maxx = rect->xmax, maxy = rect->ymax;
711   const float minxi = minx + U.pixelsize; /* boundbox inner */
712   const float maxxi = maxx - U.pixelsize;
713   const float minyi = miny + U.pixelsize;
714   const float maxyi = maxy - U.pixelsize;
715   /* for uv, can divide by zero */
716   const float facxi = (maxxi != minxi) ? 1.0f / (maxxi - minxi) : 0.0f;
717   const float facyi = (maxyi != minyi) ? 1.0f / (maxyi - minyi) : 0.0f;
718   int a, tot = 0, minsize;
719   const int hnum = ((roundboxalign & (UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT)) ==
720                         (UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT) ||
721                     (roundboxalign & (UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT)) ==
722                         (UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT)) ?
723                        1 :
724                        2;
725   const int vnum = ((roundboxalign & (UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT)) ==
726                         (UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT) ||
727                     (roundboxalign & (UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT)) ==
728                         (UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT)) ?
729                        1 :
730                        2;
731 
732   minsize = min_ii(BLI_rcti_size_x(rect) * hnum, BLI_rcti_size_y(rect) * vnum);
733 
734   if (2.0f * rad > minsize) {
735     rad = 0.5f * minsize;
736   }
737 
738   if (2.0f * (radi + 1.0f) > minsize) {
739     radi = 0.5f * minsize - U.pixelsize;
740   }
741 
742   wt->uniform_params.rad = rad;
743   wt->uniform_params.radi = radi;
744   wt->uniform_params.facxi = facxi;
745   wt->uniform_params.facyi = facyi;
746   wt->uniform_params.round_corners[0] = (roundboxalign & UI_CNR_BOTTOM_LEFT) ? 1.0f : 0.0f;
747   wt->uniform_params.round_corners[1] = (roundboxalign & UI_CNR_BOTTOM_RIGHT) ? 1.0f : 0.0f;
748   wt->uniform_params.round_corners[2] = (roundboxalign & UI_CNR_TOP_RIGHT) ? 1.0f : 0.0f;
749   wt->uniform_params.round_corners[3] = (roundboxalign & UI_CNR_TOP_LEFT) ? 1.0f : 0.0f;
750   BLI_rctf_rcti_copy(&wt->uniform_params.rect, rect);
751   BLI_rctf_init(&wt->uniform_params.recti, minxi, maxxi, minyi, maxyi);
752 
753   /* mult */
754   for (a = 0; a < WIDGET_CURVE_RESOLU; a++) {
755     veci[a][0] = radi * cornervec[a][0];
756     veci[a][1] = radi * cornervec[a][1];
757     vec[a][0] = rad * cornervec[a][0];
758     vec[a][1] = rad * cornervec[a][1];
759   }
760 
761   /* corner left-bottom */
762   if (roundboxalign & UI_CNR_BOTTOM_LEFT) {
763 
764     for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
765       wt->inner_v[tot][0] = minxi + veci[a][1];
766       wt->inner_v[tot][1] = minyi + radi - veci[a][0];
767 
768       wt->outer_v[tot][0] = minx + vec[a][1];
769       wt->outer_v[tot][1] = miny + rad - vec[a][0];
770 
771       wt->inner_uv[tot][0] = facxi * (wt->inner_v[tot][0] - minxi);
772       wt->inner_uv[tot][1] = facyi * (wt->inner_v[tot][1] - minyi);
773     }
774   }
775   else {
776     wt->inner_v[tot][0] = minxi;
777     wt->inner_v[tot][1] = minyi;
778 
779     wt->outer_v[tot][0] = minx;
780     wt->outer_v[tot][1] = miny;
781 
782     wt->inner_uv[tot][0] = 0.0f;
783     wt->inner_uv[tot][1] = 0.0f;
784 
785     tot++;
786   }
787 
788   /* corner right-bottom */
789   if (roundboxalign & UI_CNR_BOTTOM_RIGHT) {
790 
791     for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
792       wt->inner_v[tot][0] = maxxi - radi + veci[a][0];
793       wt->inner_v[tot][1] = minyi + veci[a][1];
794 
795       wt->outer_v[tot][0] = maxx - rad + vec[a][0];
796       wt->outer_v[tot][1] = miny + vec[a][1];
797 
798       wt->inner_uv[tot][0] = facxi * (wt->inner_v[tot][0] - minxi);
799       wt->inner_uv[tot][1] = facyi * (wt->inner_v[tot][1] - minyi);
800     }
801   }
802   else {
803     wt->inner_v[tot][0] = maxxi;
804     wt->inner_v[tot][1] = minyi;
805 
806     wt->outer_v[tot][0] = maxx;
807     wt->outer_v[tot][1] = miny;
808 
809     wt->inner_uv[tot][0] = 1.0f;
810     wt->inner_uv[tot][1] = 0.0f;
811 
812     tot++;
813   }
814 
815   wt->halfwayvert = tot;
816 
817   /* corner right-top */
818   if (roundboxalign & UI_CNR_TOP_RIGHT) {
819 
820     for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
821       wt->inner_v[tot][0] = maxxi - veci[a][1];
822       wt->inner_v[tot][1] = maxyi - radi + veci[a][0];
823 
824       wt->outer_v[tot][0] = maxx - vec[a][1];
825       wt->outer_v[tot][1] = maxy - rad + vec[a][0];
826 
827       wt->inner_uv[tot][0] = facxi * (wt->inner_v[tot][0] - minxi);
828       wt->inner_uv[tot][1] = facyi * (wt->inner_v[tot][1] - minyi);
829     }
830   }
831   else {
832     wt->inner_v[tot][0] = maxxi;
833     wt->inner_v[tot][1] = maxyi;
834 
835     wt->outer_v[tot][0] = maxx;
836     wt->outer_v[tot][1] = maxy;
837 
838     wt->inner_uv[tot][0] = 1.0f;
839     wt->inner_uv[tot][1] = 1.0f;
840 
841     tot++;
842   }
843 
844   /* corner left-top */
845   if (roundboxalign & UI_CNR_TOP_LEFT) {
846 
847     for (a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
848       wt->inner_v[tot][0] = minxi + radi - veci[a][0];
849       wt->inner_v[tot][1] = maxyi - veci[a][1];
850 
851       wt->outer_v[tot][0] = minx + rad - vec[a][0];
852       wt->outer_v[tot][1] = maxy - vec[a][1];
853 
854       wt->inner_uv[tot][0] = facxi * (wt->inner_v[tot][0] - minxi);
855       wt->inner_uv[tot][1] = facyi * (wt->inner_v[tot][1] - minyi);
856     }
857   }
858   else {
859 
860     wt->inner_v[tot][0] = minxi;
861     wt->inner_v[tot][1] = maxyi;
862 
863     wt->outer_v[tot][0] = minx;
864     wt->outer_v[tot][1] = maxy;
865 
866     wt->inner_uv[tot][0] = 0.0f;
867     wt->inner_uv[tot][1] = 1.0f;
868 
869     tot++;
870   }
871 
872   BLI_assert(tot <= WIDGET_SIZE_MAX);
873 
874   wt->totvert = tot;
875 }
876 
round_box_edges(uiWidgetBase * wt,int roundboxalign,const rcti * rect,float rad)877 static void round_box_edges(uiWidgetBase *wt, int roundboxalign, const rcti *rect, float rad)
878 {
879   round_box__edges(wt, roundboxalign, rect, rad, rad - U.pixelsize);
880 }
881 
882 /** \} */
883 
884 /* -------------------------------------------------------------------- */
885 /** \name Shape Preset Mini API
886  * \{ */
887 
888 /* based on button rect, return scaled array of triangles */
shape_preset_init_trias_ex(uiWidgetTrias * tria,const rcti * rect,float triasize,char where,const float verts[][2],const int verts_tot,const uint tris[][3],const int tris_tot)889 static void shape_preset_init_trias_ex(uiWidgetTrias *tria,
890                                        const rcti *rect,
891                                        float triasize,
892                                        char where,
893                                        /* input data */
894                                        const float verts[][2],
895                                        const int verts_tot,
896                                        const uint tris[][3],
897                                        const int tris_tot)
898 {
899   float centx, centy, sizex, sizey, minsize;
900   int a, i1 = 0, i2 = 1;
901 
902   if (where == 'r' || where == 'l') {
903     minsize = BLI_rcti_size_y(rect);
904   }
905   else {
906     minsize = BLI_rcti_size_x(rect);
907   }
908 
909   /* center position and size */
910   centx = (float)rect->xmin + 0.4f * minsize;
911   centy = (float)rect->ymin + 0.5f * minsize;
912   tria->size = sizex = sizey = -0.5f * triasize * minsize;
913 
914   if (where == 'r') {
915     centx = (float)rect->xmax - 0.4f * minsize;
916     sizex = -sizex;
917   }
918   else if (where == 't') {
919     centx = (float)rect->xmin + 0.5f * minsize;
920     centy = (float)rect->ymax - 0.5f * minsize;
921     sizey = -sizey;
922     i2 = 0;
923     i1 = 1;
924   }
925   else if (where == 'b') {
926     centx = (float)rect->xmin + 0.5f * minsize;
927     sizex = -sizex;
928     i2 = 0;
929     i1 = 1;
930   }
931 
932   for (a = 0; a < verts_tot; a++) {
933     tria->vec[a][0] = sizex * verts[a][i1] + centx;
934     tria->vec[a][1] = sizey * verts[a][i2] + centy;
935   }
936 
937   tria->center[0] = centx;
938   tria->center[1] = centy;
939 
940   tria->tot = tris_tot;
941   tria->index = tris;
942 }
943 
shape_preset_init_number_arrows(uiWidgetTrias * tria,const rcti * rect,float triasize,char where)944 static void shape_preset_init_number_arrows(uiWidgetTrias *tria,
945                                             const rcti *rect,
946                                             float triasize,
947                                             char where)
948 {
949   tria->type = ROUNDBOX_TRIA_ARROWS;
950   shape_preset_init_trias_ex(tria,
951                              rect,
952                              triasize,
953                              where,
954                              g_shape_preset_number_arrow_vert,
955                              ARRAY_SIZE(g_shape_preset_number_arrow_vert),
956                              g_shape_preset_number_arrow_face,
957                              ARRAY_SIZE(g_shape_preset_number_arrow_face));
958 }
959 
shape_preset_init_hold_action(uiWidgetTrias * tria,const rcti * rect,float triasize,char where)960 static void shape_preset_init_hold_action(uiWidgetTrias *tria,
961                                           const rcti *rect,
962                                           float triasize,
963                                           char where)
964 {
965   tria->type = ROUNDBOX_TRIA_HOLD_ACTION_ARROW;
966   /* With the current changes to use batches for widget drawing, the code
967    * below is doing almost nothing effectively. 'where' doesn't work either,
968    * shader is currently hardcoded to work for the button triangle pointing
969    * at the lower right. The same limitation applies to other trias as well.
970    * XXX Should be addressed. */
971   shape_preset_init_trias_ex(tria,
972                              rect,
973                              triasize,
974                              where,
975                              g_shape_preset_hold_action_vert,
976                              ARRAY_SIZE(g_shape_preset_hold_action_vert),
977                              g_shape_preset_hold_action_face,
978                              ARRAY_SIZE(g_shape_preset_hold_action_face));
979 }
980 
shape_preset_init_scroll_circle(uiWidgetTrias * tria,const rcti * rect,float triasize,char where)981 static void shape_preset_init_scroll_circle(uiWidgetTrias *tria,
982                                             const rcti *rect,
983                                             float triasize,
984                                             char where)
985 {
986   tria->type = ROUNDBOX_TRIA_SCROLL;
987   shape_preset_init_trias_ex(tria,
988                              rect,
989                              triasize,
990                              where,
991                              g_shape_preset_scroll_circle_vert,
992                              ARRAY_SIZE(g_shape_preset_scroll_circle_vert),
993                              g_shape_preset_scroll_circle_face,
994                              ARRAY_SIZE(g_shape_preset_scroll_circle_face));
995 }
996 
widget_draw_vertex_buffer(uint pos,uint col,int mode,const float quads_pos[WIDGET_SIZE_MAX][2],const uchar quads_col[WIDGET_SIZE_MAX][4],uint totvert)997 static void widget_draw_vertex_buffer(uint pos,
998                                       uint col,
999                                       int mode,
1000                                       const float quads_pos[WIDGET_SIZE_MAX][2],
1001                                       const uchar quads_col[WIDGET_SIZE_MAX][4],
1002                                       uint totvert)
1003 {
1004   immBegin(mode, totvert);
1005   for (int i = 0; i < totvert; i++) {
1006     if (quads_col) {
1007       immAttr4ubv(col, quads_col[i]);
1008     }
1009     immVertex2fv(pos, quads_pos[i]);
1010   }
1011   immEnd();
1012 }
1013 
shape_preset_trias_from_rect_menu(uiWidgetTrias * tria,const rcti * rect)1014 static void shape_preset_trias_from_rect_menu(uiWidgetTrias *tria, const rcti *rect)
1015 {
1016   const float width = BLI_rcti_size_x(rect);
1017   const float height = BLI_rcti_size_y(rect);
1018   float centx, centy, size;
1019 
1020   tria->type = ROUNDBOX_TRIA_MENU;
1021 
1022   /* Center position and size. */
1023   tria->center[0] = centx = rect->xmin + 0.52f * BLI_rcti_size_y(rect);
1024   tria->center[1] = centy = rect->ymin + 0.52f * BLI_rcti_size_y(rect);
1025   tria->size = size = 0.4f * height;
1026 
1027   if (width > height * 1.1f) {
1028     /* For wider buttons align tighter to the right. */
1029     tria->center[0] = centx = rect->xmax - 0.32f * height;
1030   }
1031 
1032   for (int a = 0; a < 6; a++) {
1033     tria->vec[a][0] = size * g_shape_preset_menu_arrow_vert[a][0] + centx;
1034     tria->vec[a][1] = size * g_shape_preset_menu_arrow_vert[a][1] + centy;
1035   }
1036 
1037   tria->tot = 2;
1038   tria->index = g_shape_preset_menu_arrow_face;
1039 }
1040 
shape_preset_trias_from_rect_checkmark(uiWidgetTrias * tria,const rcti * rect)1041 static void shape_preset_trias_from_rect_checkmark(uiWidgetTrias *tria, const rcti *rect)
1042 {
1043   float centx, centy, size;
1044 
1045   tria->type = ROUNDBOX_TRIA_CHECK;
1046 
1047   /* Center position and size. */
1048   tria->center[0] = centx = rect->xmin + 0.5f * BLI_rcti_size_y(rect);
1049   tria->center[1] = centy = rect->ymin + 0.5f * BLI_rcti_size_y(rect);
1050   tria->size = size = 0.5f * BLI_rcti_size_y(rect);
1051 
1052   for (int a = 0; a < 6; a++) {
1053     tria->vec[a][0] = size * g_shape_preset_checkmark_vert[a][0] + centx;
1054     tria->vec[a][1] = size * g_shape_preset_checkmark_vert[a][1] + centy;
1055   }
1056 
1057   tria->tot = 4;
1058   tria->index = g_shape_preset_checkmark_face;
1059 }
1060 
1061 /** \} */
1062 
1063 /* -------------------------------------------------------------------- */
1064 /** \name Widget Base Drawing
1065  * \{ */
1066 
1067 /* prepares shade colors */
shadecolors4(uchar coltop[4],uchar coldown[4],const uchar * color,short shadetop,short shadedown)1068 static void shadecolors4(
1069     uchar coltop[4], uchar coldown[4], const uchar *color, short shadetop, short shadedown)
1070 {
1071   coltop[0] = CLAMPIS(color[0] + shadetop, 0, 255);
1072   coltop[1] = CLAMPIS(color[1] + shadetop, 0, 255);
1073   coltop[2] = CLAMPIS(color[2] + shadetop, 0, 255);
1074   coltop[3] = color[3];
1075 
1076   coldown[0] = CLAMPIS(color[0] + shadedown, 0, 255);
1077   coldown[1] = CLAMPIS(color[1] + shadedown, 0, 255);
1078   coldown[2] = CLAMPIS(color[2] + shadedown, 0, 255);
1079   coldown[3] = color[3];
1080 }
1081 
widget_verts_to_triangle_strip(uiWidgetBase * wtb,const int totvert,float triangle_strip[WIDGET_SIZE_MAX * 2+2][2])1082 static void widget_verts_to_triangle_strip(uiWidgetBase *wtb,
1083                                            const int totvert,
1084                                            float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2])
1085 {
1086   int a;
1087   for (a = 0; a < totvert; a++) {
1088     copy_v2_v2(triangle_strip[a * 2], wtb->outer_v[a]);
1089     copy_v2_v2(triangle_strip[a * 2 + 1], wtb->inner_v[a]);
1090   }
1091   copy_v2_v2(triangle_strip[a * 2], wtb->outer_v[0]);
1092   copy_v2_v2(triangle_strip[a * 2 + 1], wtb->inner_v[0]);
1093 }
1094 
widgetbase_outline(uiWidgetBase * wtb,uint pos)1095 static void widgetbase_outline(uiWidgetBase *wtb, uint pos)
1096 {
1097   float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2]; /* + 2 because the last pair is wrapped */
1098   widget_verts_to_triangle_strip(wtb, wtb->totvert, triangle_strip);
1099 
1100   widget_draw_vertex_buffer(
1101       pos, 0, GPU_PRIM_TRI_STRIP, triangle_strip, NULL, wtb->totvert * 2 + 2);
1102 }
1103 
widgetbase_set_uniform_alpha_discard(uiWidgetBase * wtb,const bool alpha_check,const float discard_factor)1104 static void widgetbase_set_uniform_alpha_discard(uiWidgetBase *wtb,
1105                                                  const bool alpha_check,
1106                                                  const float discard_factor)
1107 {
1108   if (alpha_check) {
1109     wtb->uniform_params.alpha_discard = -discard_factor;
1110   }
1111   else {
1112     wtb->uniform_params.alpha_discard = discard_factor;
1113   }
1114 }
1115 
widgetbase_set_uniform_alpha_check(uiWidgetBase * wtb,const bool alpha_check)1116 static void widgetbase_set_uniform_alpha_check(uiWidgetBase *wtb, const bool alpha_check)
1117 {
1118   const float discard_factor = fabs(wtb->uniform_params.alpha_discard);
1119   widgetbase_set_uniform_alpha_discard(wtb, alpha_check, discard_factor);
1120 }
1121 
widgetbase_set_uniform_discard_factor(uiWidgetBase * wtb,const float discard_factor)1122 static void widgetbase_set_uniform_discard_factor(uiWidgetBase *wtb, const float discard_factor)
1123 {
1124   const bool alpha_check = wtb->uniform_params.alpha_discard < 0.0f;
1125   widgetbase_set_uniform_alpha_discard(wtb, alpha_check, discard_factor);
1126 }
1127 
widgetbase_set_uniform_colors_ubv(uiWidgetBase * wtb,const uchar * col1,const uchar * col2,const uchar * outline,const uchar * emboss,const uchar * tria,const bool alpha_check)1128 static void widgetbase_set_uniform_colors_ubv(uiWidgetBase *wtb,
1129                                               const uchar *col1,
1130                                               const uchar *col2,
1131                                               const uchar *outline,
1132                                               const uchar *emboss,
1133                                               const uchar *tria,
1134                                               const bool alpha_check)
1135 {
1136   widgetbase_set_uniform_alpha_check(wtb, alpha_check);
1137   rgba_float_args_set_ch(wtb->uniform_params.color_inner1, col1[0], col1[1], col1[2], col1[3]);
1138   rgba_float_args_set_ch(wtb->uniform_params.color_inner2, col2[0], col2[1], col2[2], col2[3]);
1139   rgba_float_args_set_ch(
1140       wtb->uniform_params.color_outline, outline[0], outline[1], outline[2], outline[3]);
1141   rgba_float_args_set_ch(
1142       wtb->uniform_params.color_emboss, emboss[0], emboss[1], emboss[2], emboss[3]);
1143   rgba_float_args_set_ch(wtb->uniform_params.color_tria, tria[0], tria[1], tria[2], tria[3]);
1144 }
1145 
1146 /** \} */
1147 
1148 /* -------------------------------------------------------------------- */
1149 /** \name Widget Base Drawing #GPUBatch Cache
1150  * \{ */
1151 
1152 /* keep in sync with shader */
1153 #define MAX_WIDGET_BASE_BATCH 6
1154 #define MAX_WIDGET_PARAMETERS 12
1155 
1156 static struct {
1157   uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH];
1158   int count;
1159   bool enabled;
1160 } g_widget_base_batch = {{{{0}}}};
1161 
UI_widgetbase_draw_cache_flush(void)1162 void UI_widgetbase_draw_cache_flush(void)
1163 {
1164   const float checker_params[3] = {
1165       UI_ALPHA_CHECKER_DARK / 255.0f, UI_ALPHA_CHECKER_LIGHT / 255.0f, 8.0f};
1166 
1167   if (g_widget_base_batch.count == 0) {
1168     return;
1169   }
1170 
1171   GPUBatch *batch = ui_batch_roundbox_widget_get();
1172   if (g_widget_base_batch.count == 1) {
1173     /* draw single */
1174     GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
1175     GPU_batch_uniform_4fv_array(
1176         batch, "parameters", MAX_WIDGET_PARAMETERS, (float(*)[4])g_widget_base_batch.params);
1177     GPU_batch_uniform_3fv(batch, "checkerColorAndSize", checker_params);
1178     GPU_batch_draw(batch);
1179   }
1180   else {
1181     GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE_INST);
1182     GPU_batch_uniform_4fv_array(batch,
1183                                 "parameters",
1184                                 MAX_WIDGET_PARAMETERS * MAX_WIDGET_BASE_BATCH,
1185                                 (float(*)[4])g_widget_base_batch.params);
1186     GPU_batch_uniform_3fv(batch, "checkerColorAndSize", checker_params);
1187     GPU_batch_draw_instanced(batch, g_widget_base_batch.count);
1188   }
1189   g_widget_base_batch.count = 0;
1190 }
1191 
UI_widgetbase_draw_cache_begin(void)1192 void UI_widgetbase_draw_cache_begin(void)
1193 {
1194   BLI_assert(g_widget_base_batch.enabled == false);
1195   g_widget_base_batch.enabled = true;
1196 }
1197 
UI_widgetbase_draw_cache_end(void)1198 void UI_widgetbase_draw_cache_end(void)
1199 {
1200   BLI_assert(g_widget_base_batch.enabled == true);
1201   g_widget_base_batch.enabled = false;
1202 
1203   GPU_blend(GPU_BLEND_ALPHA);
1204 
1205   UI_widgetbase_draw_cache_flush();
1206 
1207   GPU_blend(GPU_BLEND_NONE);
1208 }
1209 
1210 /* Disable cached/instanced drawing and enforce single widget drawing pipeline.
1211  * Works around interface artifacts happening on certain driver and hardware
1212  * configurations. */
draw_widgetbase_batch_skip_draw_cache(void)1213 static bool draw_widgetbase_batch_skip_draw_cache(void)
1214 {
1215   /* MacOS is known to have issues on Mac Mini and MacBook Pro with Intel Iris GPU.
1216    * For example, T78307. */
1217   if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_MAC, GPU_DRIVER_ANY)) {
1218     return true;
1219   }
1220 
1221   /* There are also reports that some AMD and Mesa driver configuration suffer from the
1222    * same issue, T78803. */
1223   if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE)) {
1224     return true;
1225   }
1226 
1227   return false;
1228 }
1229 
draw_widgetbase_batch(uiWidgetBase * wtb)1230 static void draw_widgetbase_batch(uiWidgetBase *wtb)
1231 {
1232   wtb->uniform_params.tria_type = wtb->tria1.type;
1233   wtb->uniform_params.tria1_size = wtb->tria1.size;
1234   wtb->uniform_params.tria2_size = wtb->tria2.size;
1235   copy_v2_v2(wtb->uniform_params.tria1_center, wtb->tria1.center);
1236   copy_v2_v2(wtb->uniform_params.tria2_center, wtb->tria2.center);
1237 
1238   if (g_widget_base_batch.enabled && !draw_widgetbase_batch_skip_draw_cache()) {
1239     g_widget_base_batch.params[g_widget_base_batch.count] = wtb->uniform_params;
1240     g_widget_base_batch.count++;
1241 
1242     if (g_widget_base_batch.count == MAX_WIDGET_BASE_BATCH) {
1243       UI_widgetbase_draw_cache_flush();
1244     }
1245   }
1246   else {
1247     const float checker_params[3] = {
1248         UI_ALPHA_CHECKER_DARK / 255.0f, UI_ALPHA_CHECKER_LIGHT / 255.0f, 8.0f};
1249     /* draw single */
1250     GPUBatch *batch = ui_batch_roundbox_widget_get();
1251     GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
1252     GPU_batch_uniform_4fv_array(
1253         batch, "parameters", MAX_WIDGET_PARAMETERS, (float(*)[4]) & wtb->uniform_params);
1254     GPU_batch_uniform_3fv(batch, "checkerColorAndSize", checker_params);
1255     GPU_batch_draw(batch);
1256   }
1257 }
1258 
widgetbase_draw_ex(uiWidgetBase * wtb,const uiWidgetColors * wcol,bool show_alpha_checkers)1259 static void widgetbase_draw_ex(uiWidgetBase *wtb,
1260                                const uiWidgetColors *wcol,
1261                                bool show_alpha_checkers)
1262 {
1263   uchar inner_col1[4] = {0};
1264   uchar inner_col2[4] = {0};
1265   uchar emboss_col[4] = {0};
1266   uchar outline_col[4] = {0};
1267   uchar tria_col[4] = {0};
1268   /* For color widget. */
1269   if (wcol->shaded != 0) {
1270     show_alpha_checkers = false;
1271   }
1272 
1273   /* backdrop non AA */
1274   if (wtb->draw_inner) {
1275     if (wcol->shaded == 0) {
1276       /* simple fill */
1277       inner_col1[0] = inner_col2[0] = wcol->inner[0];
1278       inner_col1[1] = inner_col2[1] = wcol->inner[1];
1279       inner_col1[2] = inner_col2[2] = wcol->inner[2];
1280       inner_col1[3] = inner_col2[3] = wcol->inner[3];
1281     }
1282     else {
1283       /* gradient fill */
1284       shadecolors4(inner_col1, inner_col2, wcol->inner, wcol->shadetop, wcol->shadedown);
1285     }
1286   }
1287 
1288   if (wtb->draw_outline) {
1289     outline_col[0] = wcol->outline[0];
1290     outline_col[1] = wcol->outline[1];
1291     outline_col[2] = wcol->outline[2];
1292     outline_col[3] = wcol->outline[3];
1293 
1294     /* emboss bottom shadow */
1295     if (wtb->draw_emboss) {
1296       UI_GetThemeColor4ubv(TH_WIDGET_EMBOSS, emboss_col);
1297     }
1298   }
1299 
1300   if (wtb->tria1.type != ROUNDBOX_TRIA_NONE) {
1301     tria_col[0] = wcol->item[0];
1302     tria_col[1] = wcol->item[1];
1303     tria_col[2] = wcol->item[2];
1304     tria_col[3] = wcol->item[3];
1305   }
1306 
1307   /* Draw everything in one drawcall */
1308   if (inner_col1[3] || inner_col2[3] || outline_col[3] || emboss_col[3] || tria_col[3] ||
1309       show_alpha_checkers) {
1310     widgetbase_set_uniform_colors_ubv(
1311         wtb, inner_col1, inner_col2, outline_col, emboss_col, tria_col, show_alpha_checkers);
1312 
1313     GPU_blend(GPU_BLEND_ALPHA);
1314     draw_widgetbase_batch(wtb);
1315     GPU_blend(GPU_BLEND_NONE);
1316   }
1317 }
1318 
widgetbase_draw(uiWidgetBase * wtb,const uiWidgetColors * wcol)1319 static void widgetbase_draw(uiWidgetBase *wtb, const uiWidgetColors *wcol)
1320 {
1321   widgetbase_draw_ex(wtb, wcol, false);
1322 }
1323 
1324 /** \} */
1325 
1326 /* -------------------------------------------------------------------- */
1327 /** \name Text/Icon Drawing
1328  * \{ */
1329 
1330 #define UI_TEXT_CLIP_MARGIN (0.25f * U.widget_unit / but->block->aspect)
1331 
1332 #define PREVIEW_PAD 4
1333 
widget_alpha_factor(const int state)1334 static float widget_alpha_factor(const int state)
1335 {
1336   if (state & (UI_BUT_INACTIVE | UI_BUT_DISABLED)) {
1337     if (state & UI_SEARCH_FILTER_NO_MATCH) {
1338       return 0.25f;
1339     }
1340     return 0.5f;
1341   }
1342 
1343   if (state & UI_SEARCH_FILTER_NO_MATCH) {
1344     return 0.5f;
1345   }
1346 
1347   return 1.0f;
1348 }
1349 
widget_draw_preview(BIFIconID icon,float alpha,const rcti * rect)1350 static void widget_draw_preview(BIFIconID icon, float alpha, const rcti *rect)
1351 {
1352   int w, h, size;
1353 
1354   if (icon == ICON_NONE) {
1355     return;
1356   }
1357 
1358   w = BLI_rcti_size_x(rect);
1359   h = BLI_rcti_size_y(rect);
1360   size = MIN2(w, h);
1361   size -= PREVIEW_PAD * 2; /* padding */
1362 
1363   if (size > 0) {
1364     const int x = rect->xmin + w / 2 - size / 2;
1365     const int y = rect->ymin + h / 2 - size / 2;
1366 
1367     UI_icon_draw_preview(x, y, icon, 1.0f, alpha, size);
1368   }
1369 }
1370 
ui_but_draw_menu_icon(const uiBut * but)1371 static int ui_but_draw_menu_icon(const uiBut *but)
1372 {
1373   return (but->flag & UI_BUT_ICON_SUBMENU) && (but->emboss == UI_EMBOSS_PULLDOWN);
1374 }
1375 
1376 /* icons have been standardized... and this call draws in untransformed coordinates */
1377 
widget_draw_icon(const uiBut * but,BIFIconID icon,float alpha,const rcti * rect,const uchar mono_color[4])1378 static void widget_draw_icon(
1379     const uiBut *but, BIFIconID icon, float alpha, const rcti *rect, const uchar mono_color[4])
1380 {
1381   float xs = 0.0f, ys = 0.0f;
1382   float aspect, height;
1383 
1384   if (but->flag & UI_BUT_ICON_PREVIEW) {
1385     GPU_blend(GPU_BLEND_ALPHA);
1386     widget_draw_preview(icon, alpha, rect);
1387     GPU_blend(GPU_BLEND_NONE);
1388     return;
1389   }
1390 
1391   /* this icon doesn't need draw... */
1392   if (icon == ICON_BLANK1 && (but->flag & UI_BUT_ICON_SUBMENU) == 0) {
1393     return;
1394   }
1395 
1396   aspect = but->block->aspect * U.inv_dpi_fac;
1397   height = ICON_DEFAULT_HEIGHT / aspect;
1398 
1399   /* calculate blend color */
1400   if (ELEM(but->type, UI_BTYPE_TOGGLE, UI_BTYPE_ROW, UI_BTYPE_TOGGLE_N, UI_BTYPE_LISTROW)) {
1401     if (but->flag & UI_SELECT) {
1402       /* pass */
1403     }
1404     else if (but->flag & UI_ACTIVE) {
1405       /* pass */
1406     }
1407     else {
1408       alpha = 0.75f;
1409     }
1410   }
1411   else if ((but->type == UI_BTYPE_LABEL)) {
1412     /* extra feature allows more alpha blending */
1413     if (but->a1 == 1.0f) {
1414       alpha *= but->a2;
1415     }
1416   }
1417   else if (ELEM(but->type, UI_BTYPE_BUT, UI_BTYPE_DECORATOR)) {
1418     alpha *= widget_alpha_factor(but->flag);
1419   }
1420 
1421   GPU_blend(GPU_BLEND_ALPHA);
1422 
1423   if (icon && icon != ICON_BLANK1) {
1424     const float ofs = 1.0f / aspect;
1425 
1426     if (but->drawflag & UI_BUT_ICON_LEFT) {
1427       /* special case - icon_only pie buttons */
1428       if (ui_block_is_pie_menu(but->block) && !ELEM(but->type, UI_BTYPE_MENU, UI_BTYPE_POPOVER) &&
1429           but->str && but->str[0] == '\0') {
1430         xs = rect->xmin + 2.0f * ofs;
1431       }
1432       else if (but->emboss == UI_EMBOSS_NONE || but->type == UI_BTYPE_LABEL) {
1433         xs = rect->xmin + 2.0f * ofs;
1434       }
1435       else {
1436         xs = rect->xmin + 4.0f * ofs;
1437       }
1438     }
1439     else {
1440       xs = (rect->xmin + rect->xmax - height) / 2.0f;
1441     }
1442     ys = (rect->ymin + rect->ymax - height) / 2.0f;
1443 
1444     /* force positions to integers, for zoom levels near 1. draws icons crisp. */
1445     if (aspect > 0.95f && aspect < 1.05f) {
1446       xs = (int)(xs + 0.1f);
1447       ys = (int)(ys + 0.1f);
1448     }
1449 
1450     /* Get theme color. */
1451     uchar color[4] = {mono_color[0], mono_color[1], mono_color[2], mono_color[3]};
1452     const bool has_theme = UI_icon_get_theme_color(icon, color);
1453 
1454     /* to indicate draggable */
1455     if (but->dragpoin && (but->flag & UI_ACTIVE)) {
1456       UI_icon_draw_ex(xs, ys, icon, aspect, 1.25f, 0.0f, color, has_theme);
1457     }
1458     else if ((but->flag & (UI_ACTIVE | UI_SELECT | UI_SELECT_DRAW))) {
1459       UI_icon_draw_ex(xs, ys, icon, aspect, alpha, 0.0f, color, has_theme);
1460     }
1461     else if (!((but->icon != ICON_NONE) && UI_but_is_tool(but))) {
1462       if (has_theme) {
1463         alpha *= 0.8f;
1464       }
1465       UI_icon_draw_ex(xs, ys, icon, aspect, alpha, 0.0f, color, has_theme);
1466     }
1467     else {
1468       const bTheme *btheme = UI_GetTheme();
1469       const float desaturate = 1.0 - btheme->tui.icon_saturation;
1470       UI_icon_draw_ex(xs, ys, icon, aspect, alpha, desaturate, color, has_theme);
1471     }
1472   }
1473 
1474   GPU_blend(GPU_BLEND_NONE);
1475 }
1476 
widget_draw_submenu_tria(const uiBut * but,const rcti * rect,const uiWidgetColors * wcol)1477 static void widget_draw_submenu_tria(const uiBut *but,
1478                                      const rcti *rect,
1479                                      const uiWidgetColors *wcol)
1480 {
1481   const float aspect = but->block->aspect * U.inv_dpi_fac;
1482   const int tria_height = (int)(ICON_DEFAULT_HEIGHT / aspect);
1483   const int tria_width = (int)(ICON_DEFAULT_WIDTH / aspect) - 2 * U.pixelsize;
1484   const int xs = rect->xmax - tria_width;
1485   const int ys = (rect->ymin + rect->ymax - tria_height) / 2.0f;
1486   float col[4];
1487   rctf tria_rect;
1488 
1489   rgba_uchar_to_float(col, wcol->text);
1490   col[3] *= 0.8f;
1491 
1492   BLI_rctf_init(&tria_rect, xs, xs + tria_width, ys, ys + tria_height);
1493   BLI_rctf_scale(&tria_rect, 0.4f);
1494 
1495   GPU_blend(GPU_BLEND_ALPHA);
1496   UI_widgetbase_draw_cache_flush();
1497   GPU_blend(GPU_BLEND_NONE);
1498   ui_draw_anti_tria_rect(&tria_rect, 'h', col);
1499 }
1500 
ui_text_clip_give_prev_off(uiBut * but,const char * str)1501 static void ui_text_clip_give_prev_off(uiBut *but, const char *str)
1502 {
1503   const char *prev_utf8 = BLI_str_find_prev_char_utf8(str, str + but->ofs);
1504   const int bytes = str + but->ofs - prev_utf8;
1505 
1506   but->ofs -= bytes;
1507 }
1508 
ui_text_clip_give_next_off(uiBut * but,const char * str)1509 static void ui_text_clip_give_next_off(uiBut *but, const char *str)
1510 {
1511   const char *next_utf8 = BLI_str_find_next_char_utf8(str + but->ofs, NULL);
1512   const int bytes = next_utf8 - (str + but->ofs);
1513 
1514   but->ofs += bytes;
1515 }
1516 
1517 /**
1518  * Helper.
1519  * This func assumes things like kerning handling have already been handled!
1520  * Return the length of modified (right-clipped + ellipsis) string.
1521  */
ui_text_clip_right_ex(const uiFontStyle * fstyle,char * str,const size_t max_len,const float okwidth,const char * sep,const int sep_len,const float sep_strwidth,size_t * r_final_len)1522 static void ui_text_clip_right_ex(const uiFontStyle *fstyle,
1523                                   char *str,
1524                                   const size_t max_len,
1525                                   const float okwidth,
1526                                   const char *sep,
1527                                   const int sep_len,
1528                                   const float sep_strwidth,
1529                                   size_t *r_final_len)
1530 {
1531   float tmp;
1532   int l_end;
1533 
1534   BLI_assert(str[0]);
1535 
1536   /* If the trailing ellipsis takes more than 20% of all available width, just cut the string
1537    * (as using the ellipsis would remove even more useful chars, and we cannot show much
1538    * already!).
1539    */
1540   if (sep_strwidth / okwidth > 0.2f) {
1541     l_end = BLF_width_to_strlen(fstyle->uifont_id, str, max_len, okwidth, &tmp);
1542     str[l_end] = '\0';
1543     if (r_final_len) {
1544       *r_final_len = (size_t)l_end;
1545     }
1546   }
1547   else {
1548     l_end = BLF_width_to_strlen(fstyle->uifont_id, str, max_len, okwidth - sep_strwidth, &tmp);
1549     memcpy(str + l_end, sep, sep_len + 1); /* +1 for trailing '\0'. */
1550     if (r_final_len) {
1551       *r_final_len = (size_t)(l_end) + sep_len;
1552     }
1553   }
1554 }
1555 
1556 /**
1557  * Cut off the middle of the text to fit into the given width.
1558  *
1559  * \note in case this middle clipping would just remove a few chars,
1560  * it rather clips right, which is more readable.
1561  *
1562  * If rpart_sep is not Null, the part of str starting to first occurrence of rpart_sep
1563  * is preserved at all cost.
1564  * Useful for strings with shortcuts
1565  * (like 'AVeryLongFooBarLabelForMenuEntry|Ctrl O' -> 'AVeryLong...MenuEntry|Ctrl O').
1566  */
UI_text_clip_middle_ex(const uiFontStyle * fstyle,char * str,float okwidth,const float minwidth,const size_t max_len,const char rpart_sep)1567 float UI_text_clip_middle_ex(const uiFontStyle *fstyle,
1568                              char *str,
1569                              float okwidth,
1570                              const float minwidth,
1571                              const size_t max_len,
1572                              const char rpart_sep)
1573 {
1574   float strwidth;
1575 
1576   /* Add some epsilon to OK width, avoids 'ellipsing' text that nearly fits!
1577    * Better to have a small piece of the last char cut out,
1578    * than two remaining chars replaced by an ellipsis... */
1579   okwidth += 1.0f + UI_DPI_FAC;
1580 
1581   BLI_assert(str[0]);
1582 
1583   /* need to set this first */
1584   UI_fontstyle_set(fstyle);
1585 
1586   if (fstyle->kerning == 1) {
1587     /* for BLF_width */
1588     BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
1589   }
1590 
1591   strwidth = BLF_width(fstyle->uifont_id, str, max_len);
1592 
1593   if ((okwidth > 0.0f) && (strwidth > okwidth)) {
1594     /* Ellipsis. Some compilers complain with real literal string. */
1595     const char sep[] = {0xe2, 0x80, 0xA6, 0x0};
1596     const int sep_len = sizeof(sep) - 1;
1597     const float sep_strwidth = BLF_width(fstyle->uifont_id, sep, sep_len + 1);
1598     float parts_strwidth;
1599     size_t l_end;
1600 
1601     char *rpart = NULL, rpart_buf[UI_MAX_DRAW_STR];
1602     float rpart_width = 0.0f;
1603     size_t rpart_len = 0;
1604     size_t final_lpart_len;
1605 
1606     if (rpart_sep) {
1607       rpart = strrchr(str, rpart_sep);
1608 
1609       if (rpart) {
1610         rpart_len = strlen(rpart);
1611         rpart_width = BLF_width(fstyle->uifont_id, rpart, rpart_len);
1612         okwidth -= rpart_width;
1613         strwidth -= rpart_width;
1614 
1615         if (okwidth < 0.0f) {
1616           /* Not enough place for actual label, just display protected right part.
1617            * Here just for safety, should never happen in real life! */
1618           memmove(str, rpart, rpart_len + 1);
1619           rpart = NULL;
1620           okwidth += rpart_width;
1621           strwidth = rpart_width;
1622         }
1623       }
1624     }
1625 
1626     parts_strwidth = (okwidth - sep_strwidth) / 2.0f;
1627 
1628     if (rpart) {
1629       strcpy(rpart_buf, rpart);
1630       *rpart = '\0';
1631       rpart = rpart_buf;
1632     }
1633 
1634     l_end = BLF_width_to_strlen(fstyle->uifont_id, str, max_len, parts_strwidth, NULL);
1635     if (l_end < 10 || min_ff(parts_strwidth, strwidth - okwidth) < minwidth) {
1636       /* If we really have no place, or we would clip a very small piece of string in the middle,
1637        * only show start of string.
1638        */
1639       ui_text_clip_right_ex(
1640           fstyle, str, max_len, okwidth, sep, sep_len, sep_strwidth, &final_lpart_len);
1641     }
1642     else {
1643       size_t r_offset, r_len;
1644 
1645       r_offset = BLF_width_to_rstrlen(fstyle->uifont_id, str, max_len, parts_strwidth, NULL);
1646       r_len = strlen(str + r_offset) + 1; /* +1 for the trailing '\0'. */
1647 
1648       if (l_end + sep_len + r_len + rpart_len > max_len) {
1649         /* Corner case, the str already takes all available mem,
1650          * and the ellipsis chars would actually add more chars.
1651          * Better to just trim one or two letters to the right in this case...
1652          * Note: with a single-char ellipsis, this should never happen! But better be safe
1653          * here...
1654          */
1655         ui_text_clip_right_ex(
1656             fstyle, str, max_len, okwidth, sep, sep_len, sep_strwidth, &final_lpart_len);
1657       }
1658       else {
1659         memmove(str + l_end + sep_len, str + r_offset, r_len);
1660         memcpy(str + l_end, sep, sep_len);
1661         /* -1 to remove trailing '\0'! */
1662         final_lpart_len = (size_t)(l_end + sep_len + r_len - 1);
1663 
1664         while (BLF_width(fstyle->uifont_id, str, max_len) > okwidth) {
1665           /* This will happen because a lot of string width processing is done in integer pixels,
1666            * which can introduce a rather high error in the end (about 2 pixels or so).
1667            * Only one char removal shall ever be needed in real-life situation... */
1668           r_len--;
1669           final_lpart_len--;
1670           char *c = str + l_end + sep_len;
1671           memmove(c, c + 1, r_len);
1672         }
1673       }
1674     }
1675 
1676     if (rpart) {
1677       /* Add back preserved right part to our shorten str. */
1678       memcpy(str + final_lpart_len, rpart, rpart_len + 1); /* +1 for trailing '\0'. */
1679       okwidth += rpart_width;
1680     }
1681 
1682     strwidth = BLF_width(fstyle->uifont_id, str, max_len);
1683   }
1684 
1685   if (fstyle->kerning == 1) {
1686     BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
1687   }
1688 
1689   BLI_assert(strwidth <= okwidth);
1690 
1691   return strwidth;
1692 }
1693 
1694 /**
1695  * Wrapper around UI_text_clip_middle_ex.
1696  */
ui_text_clip_middle(const uiFontStyle * fstyle,uiBut * but,const rcti * rect)1697 static void ui_text_clip_middle(const uiFontStyle *fstyle, uiBut *but, const rcti *rect)
1698 {
1699   /* No margin for labels! */
1700   const int border = ELEM(but->type, UI_BTYPE_LABEL, UI_BTYPE_MENU, UI_BTYPE_POPOVER) ?
1701                          0 :
1702                          (int)(UI_TEXT_CLIP_MARGIN + 0.5f);
1703   const float okwidth = (float)max_ii(BLI_rcti_size_x(rect) - border, 0);
1704   const size_t max_len = sizeof(but->drawstr);
1705   const float minwidth = (float)(UI_DPI_ICON_SIZE) / but->block->aspect * 2.0f;
1706 
1707   but->ofs = 0;
1708   but->strwidth = UI_text_clip_middle_ex(fstyle, but->drawstr, okwidth, minwidth, max_len, '\0');
1709 }
1710 
1711 /**
1712  * Like #ui_text_clip_middle(), but protect/preserve at all cost
1713  * the right part of the string after sep.
1714  * Useful for strings with shortcuts
1715  * (like 'AVeryLongFooBarLabelForMenuEntry|Ctrl O' -> 'AVeryLong...MenuEntry|Ctrl O').
1716  */
ui_text_clip_middle_protect_right(const uiFontStyle * fstyle,uiBut * but,const rcti * rect,const char rsep)1717 static void ui_text_clip_middle_protect_right(const uiFontStyle *fstyle,
1718                                               uiBut *but,
1719                                               const rcti *rect,
1720                                               const char rsep)
1721 {
1722   /* No margin for labels! */
1723   const int border = ELEM(but->type, UI_BTYPE_LABEL, UI_BTYPE_MENU, UI_BTYPE_POPOVER) ?
1724                          0 :
1725                          (int)(UI_TEXT_CLIP_MARGIN + 0.5f);
1726   const float okwidth = (float)max_ii(BLI_rcti_size_x(rect) - border, 0);
1727   const size_t max_len = sizeof(but->drawstr);
1728   const float minwidth = (float)(UI_DPI_ICON_SIZE) / but->block->aspect * 2.0f;
1729 
1730   but->ofs = 0;
1731   but->strwidth = UI_text_clip_middle_ex(fstyle, but->drawstr, okwidth, minwidth, max_len, rsep);
1732 }
1733 
1734 /**
1735  * Cut off the text, taking into account the cursor location (text display while editing).
1736  */
ui_text_clip_cursor(const uiFontStyle * fstyle,uiBut * but,const rcti * rect)1737 static void ui_text_clip_cursor(const uiFontStyle *fstyle, uiBut *but, const rcti *rect)
1738 {
1739   const int border = (int)(UI_TEXT_CLIP_MARGIN + 0.5f);
1740   const int okwidth = max_ii(BLI_rcti_size_x(rect) - border, 0);
1741 
1742   BLI_assert(but->editstr && but->pos >= 0);
1743 
1744   /* need to set this first */
1745   UI_fontstyle_set(fstyle);
1746 
1747   if (fstyle->kerning == 1) {
1748     /* for BLF_width */
1749     BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
1750   }
1751 
1752   /* define ofs dynamically */
1753   if (but->ofs > but->pos) {
1754     but->ofs = but->pos;
1755   }
1756 
1757   if (BLF_width(fstyle->uifont_id, but->editstr, INT_MAX) <= okwidth) {
1758     but->ofs = 0;
1759   }
1760 
1761   but->strwidth = BLF_width(fstyle->uifont_id, but->editstr + but->ofs, INT_MAX);
1762 
1763   if (but->strwidth > okwidth) {
1764     int len = strlen(but->editstr);
1765 
1766     while (but->strwidth > okwidth) {
1767       float width;
1768 
1769       /* string position of cursor */
1770       width = BLF_width(fstyle->uifont_id, but->editstr + but->ofs, (but->pos - but->ofs));
1771 
1772       /* if cursor is at 20 pixels of right side button we clip left */
1773       if (width > okwidth - 20) {
1774         ui_text_clip_give_next_off(but, but->editstr);
1775       }
1776       else {
1777         int bytes;
1778         /* shift string to the left */
1779         if (width < 20 && but->ofs > 0) {
1780           ui_text_clip_give_prev_off(but, but->editstr);
1781         }
1782         bytes = BLI_str_utf8_size(BLI_str_find_prev_char_utf8(but->editstr, but->editstr + len));
1783         if (bytes == -1) {
1784           bytes = 1;
1785         }
1786         len -= bytes;
1787       }
1788 
1789       but->strwidth = BLF_width(fstyle->uifont_id, but->editstr + but->ofs, len - but->ofs);
1790 
1791       if (but->strwidth < 10) {
1792         break;
1793       }
1794     }
1795   }
1796 
1797   if (fstyle->kerning == 1) {
1798     BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
1799   }
1800 }
1801 
1802 /**
1803  * Cut off the end of text to fit into the width of \a rect.
1804  *
1805  * \note deals with ': ' especially for number buttons
1806  */
ui_text_clip_right_label(const uiFontStyle * fstyle,uiBut * but,const rcti * rect)1807 static void ui_text_clip_right_label(const uiFontStyle *fstyle, uiBut *but, const rcti *rect)
1808 {
1809   const int border = UI_TEXT_CLIP_MARGIN + 1;
1810   const int okwidth = max_ii(BLI_rcti_size_x(rect) - border, 0);
1811   char *cpoin = NULL;
1812   int drawstr_len = strlen(but->drawstr);
1813   const char *cpend = but->drawstr + drawstr_len;
1814 
1815   /* need to set this first */
1816   UI_fontstyle_set(fstyle);
1817 
1818   if (fstyle->kerning == 1) {
1819     /* for BLF_width */
1820     BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
1821   }
1822 
1823   but->strwidth = BLF_width(fstyle->uifont_id, but->drawstr, sizeof(but->drawstr));
1824   but->ofs = 0;
1825 
1826   /* First shorten num-buttons eg,
1827    *   Translucency: 0.000
1828    * becomes
1829    *   Trans: 0.000
1830    */
1831 
1832   /* find the space after ':' separator */
1833   cpoin = strrchr(but->drawstr, ':');
1834 
1835   if (cpoin && (cpoin < cpend - 2)) {
1836     char *cp2 = cpoin;
1837 
1838     /* chop off the leading text, starting from the right */
1839     while (but->strwidth > okwidth && cp2 > but->drawstr) {
1840       const char *prev_utf8 = BLI_str_find_prev_char_utf8(but->drawstr, cp2);
1841       const int bytes = cp2 - prev_utf8;
1842 
1843       /* shift the text after and including cp2 back by 1 char,
1844        * +1 to include null terminator */
1845       memmove(cp2 - bytes, cp2, drawstr_len + 1);
1846       cp2 -= bytes;
1847 
1848       drawstr_len -= bytes;
1849       // BLI_assert(strlen(but->drawstr) == drawstr_len);
1850 
1851       but->strwidth = BLF_width(
1852           fstyle->uifont_id, but->drawstr + but->ofs, sizeof(but->drawstr) - but->ofs);
1853       if (but->strwidth < 10) {
1854         break;
1855       }
1856     }
1857 
1858     /* after the leading text is gone, chop off the : and following space, with ofs */
1859     while ((but->strwidth > okwidth) && (but->ofs < 2)) {
1860       ui_text_clip_give_next_off(but, but->drawstr);
1861       but->strwidth = BLF_width(
1862           fstyle->uifont_id, but->drawstr + but->ofs, sizeof(but->drawstr) - but->ofs);
1863       if (but->strwidth < 10) {
1864         break;
1865       }
1866     }
1867   }
1868 
1869   /* Now just remove trailing chars */
1870   /* once the label's gone, chop off the least significant digits */
1871   if (but->strwidth > okwidth) {
1872     float strwidth;
1873     drawstr_len = BLF_width_to_strlen(fstyle->uifont_id,
1874                                       but->drawstr + but->ofs,
1875                                       drawstr_len - but->ofs,
1876                                       okwidth,
1877                                       &strwidth) +
1878                   but->ofs;
1879     but->strwidth = strwidth;
1880     but->drawstr[drawstr_len] = 0;
1881   }
1882 
1883   if (fstyle->kerning == 1) {
1884     BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
1885   }
1886 }
1887 
1888 #ifdef WITH_INPUT_IME
widget_draw_text_ime_underline(const uiFontStyle * fstyle,const uiWidgetColors * wcol,const uiBut * but,const rcti * rect,const wmIMEData * ime_data,const char * drawstr)1889 static void widget_draw_text_ime_underline(const uiFontStyle *fstyle,
1890                                            const uiWidgetColors *wcol,
1891                                            const uiBut *but,
1892                                            const rcti *rect,
1893                                            const wmIMEData *ime_data,
1894                                            const char *drawstr)
1895 {
1896   int ofs_x, width;
1897   int rect_x = BLI_rcti_size_x(rect);
1898   int sel_start = ime_data->sel_start, sel_end = ime_data->sel_end;
1899   float fcol[4];
1900 
1901   if (drawstr[0] != 0) {
1902     if (but->pos >= but->ofs) {
1903       ofs_x = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->pos - but->ofs);
1904     }
1905     else {
1906       ofs_x = 0;
1907     }
1908 
1909     width = BLF_width(
1910         fstyle->uifont_id, drawstr + but->ofs, ime_data->composite_len + but->pos - but->ofs);
1911 
1912     rgba_uchar_to_float(fcol, wcol->text);
1913     UI_draw_text_underline(rect->xmin + ofs_x,
1914                            rect->ymin + 6 * U.pixelsize,
1915                            min_ii(width, rect_x - 2) - ofs_x,
1916                            1,
1917                            fcol);
1918 
1919     /* draw the thick line */
1920     if (sel_start != -1 && sel_end != -1) {
1921       sel_end -= sel_start;
1922       sel_start += but->pos;
1923 
1924       if (sel_start >= but->ofs) {
1925         ofs_x = BLF_width(fstyle->uifont_id, drawstr + but->ofs, sel_start - but->ofs);
1926       }
1927       else {
1928         ofs_x = 0;
1929       }
1930 
1931       width = BLF_width(fstyle->uifont_id, drawstr + but->ofs, sel_end + sel_start - but->ofs);
1932 
1933       UI_draw_text_underline(rect->xmin + ofs_x,
1934                              rect->ymin + 6 * U.pixelsize,
1935                              min_ii(width, rect_x - 2) - ofs_x,
1936                              2,
1937                              fcol);
1938     }
1939   }
1940 }
1941 #endif /* WITH_INPUT_IME */
1942 
widget_draw_text_underline_calc_center_x(const char * UNUSED (str),const size_t str_step_ofs,const rcti * glyph_step_bounds,const int UNUSED (glyph_advance_x),const rctf * glyph_bounds,const int glyph_bearing[2],void * user_data)1943 static bool widget_draw_text_underline_calc_center_x(const char *UNUSED(str),
1944                                                      const size_t str_step_ofs,
1945                                                      const rcti *glyph_step_bounds,
1946                                                      const int UNUSED(glyph_advance_x),
1947                                                      const rctf *glyph_bounds,
1948                                                      const int glyph_bearing[2],
1949                                                      void *user_data)
1950 {
1951   /* The index of the character to get, set to the x-position. */
1952   int *ul_data = user_data;
1953   if (ul_data[0] == (int)str_step_ofs) {
1954     ul_data[1] = glyph_step_bounds->xmin + glyph_bearing[0] +
1955                  (BLI_rctf_size_x(glyph_bounds) / 2.0f);
1956     /* Early exit. */
1957     return false;
1958   }
1959   return true;
1960 }
1961 
widget_draw_text(const uiFontStyle * fstyle,const uiWidgetColors * wcol,uiBut * but,rcti * rect)1962 static void widget_draw_text(const uiFontStyle *fstyle,
1963                              const uiWidgetColors *wcol,
1964                              uiBut *but,
1965                              rcti *rect)
1966 {
1967   int drawstr_left_len = UI_MAX_DRAW_STR;
1968   const char *drawstr = but->drawstr;
1969   const char *drawstr_right = NULL;
1970   bool use_right_only = false;
1971 
1972 #ifdef WITH_INPUT_IME
1973   const wmIMEData *ime_data;
1974 #endif
1975 
1976   UI_fontstyle_set(fstyle);
1977 
1978   eFontStyle_Align align;
1979   if (but->editstr || (but->drawflag & UI_BUT_TEXT_LEFT)) {
1980     align = UI_STYLE_TEXT_LEFT;
1981   }
1982   else if (but->drawflag & UI_BUT_TEXT_RIGHT) {
1983     align = UI_STYLE_TEXT_RIGHT;
1984   }
1985   else {
1986     align = UI_STYLE_TEXT_CENTER;
1987   }
1988 
1989   if (fstyle->kerning == 1) {
1990     /* for BLF_width */
1991     BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
1992   }
1993 
1994   /* Special case: when we're entering text for multiple buttons,
1995    * don't draw the text for any of the multi-editing buttons */
1996   if (UNLIKELY(but->flag & UI_BUT_DRAG_MULTI)) {
1997     uiBut *but_edit = ui_but_drag_multi_edit_get(but);
1998     if (but_edit) {
1999       drawstr = but_edit->editstr;
2000       align = UI_STYLE_TEXT_LEFT;
2001     }
2002   }
2003   else {
2004     if (but->editstr) {
2005       /* max length isn't used in this case,
2006        * we rely on string being NULL terminated. */
2007       drawstr_left_len = INT_MAX;
2008 
2009 #ifdef WITH_INPUT_IME
2010       /* FIXME, IME is modifying 'const char *drawstr! */
2011       ime_data = ui_but_ime_data_get(but);
2012 
2013       if (ime_data && ime_data->composite_len) {
2014         /* insert composite string into cursor pos */
2015         BLI_snprintf((char *)drawstr,
2016                      UI_MAX_DRAW_STR,
2017                      "%s%s%s",
2018                      but->editstr,
2019                      ime_data->str_composite,
2020                      but->editstr + but->pos);
2021       }
2022       else
2023 #endif
2024       {
2025         drawstr = but->editstr;
2026       }
2027     }
2028   }
2029 
2030   /* text button selection, cursor, composite underline */
2031   if (but->editstr && but->pos != -1) {
2032     int but_pos_ofs;
2033     /* Shape of the cursor for drawing. */
2034     rcti but_cursor_shape;
2035 
2036     /* text button selection */
2037     if ((but->selend - but->selsta) > 0) {
2038       int selsta_draw, selwidth_draw;
2039 
2040       if (drawstr[0] != 0) {
2041         /* We are drawing on top of widget bases. Flush cache. */
2042         GPU_blend(GPU_BLEND_ALPHA);
2043         UI_widgetbase_draw_cache_flush();
2044         GPU_blend(GPU_BLEND_NONE);
2045 
2046         if (but->selsta >= but->ofs) {
2047           selsta_draw = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->selsta - but->ofs);
2048         }
2049         else {
2050           selsta_draw = 0;
2051         }
2052 
2053         selwidth_draw = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->selend - but->ofs);
2054 
2055         uint pos = GPU_vertformat_attr_add(
2056             immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
2057         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2058 
2059         immUniformColor4ubv(wcol->item);
2060         immRecti(pos,
2061                  rect->xmin + selsta_draw,
2062                  rect->ymin + U.pixelsize,
2063                  min_ii(rect->xmin + selwidth_draw, rect->xmax - 2),
2064                  rect->ymax - U.pixelsize);
2065 
2066         immUnbindProgram();
2067       }
2068     }
2069 
2070     /* text cursor */
2071     but_pos_ofs = but->pos;
2072 
2073 #ifdef WITH_INPUT_IME
2074     /* if is ime compositing, move the cursor */
2075     if (ime_data && ime_data->composite_len && ime_data->cursor_pos != -1) {
2076       but_pos_ofs += ime_data->cursor_pos;
2077     }
2078 #endif
2079 
2080     if (but->pos >= but->ofs) {
2081       int t;
2082       if (drawstr[0] != 0) {
2083         t = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but_pos_ofs - but->ofs);
2084       }
2085       else {
2086         t = 0;
2087       }
2088       /* We are drawing on top of widget bases. Flush cache. */
2089       GPU_blend(GPU_BLEND_ALPHA);
2090       UI_widgetbase_draw_cache_flush();
2091       GPU_blend(GPU_BLEND_NONE);
2092 
2093       uint pos = GPU_vertformat_attr_add(
2094           immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
2095       immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2096 
2097       immUniformThemeColor(TH_WIDGET_TEXT_CURSOR);
2098 
2099       but_cursor_shape.xmin = (rect->xmin + t) - U.pixelsize;
2100       but_cursor_shape.ymin = rect->ymin + U.pixelsize;
2101       but_cursor_shape.xmax = (rect->xmin + t) + U.pixelsize;
2102       but_cursor_shape.ymax = rect->ymax - U.pixelsize;
2103 
2104       /* draw cursor */
2105       immRecti(pos,
2106                but_cursor_shape.xmin,
2107                but_cursor_shape.ymin,
2108                but_cursor_shape.xmax,
2109                but_cursor_shape.ymax);
2110 
2111       immUnbindProgram();
2112     }
2113 
2114 #ifdef WITH_INPUT_IME
2115     if (ime_data && ime_data->composite_len) {
2116       /* ime cursor following */
2117       if (but->pos >= but->ofs) {
2118         ui_but_ime_reposition(but, but_cursor_shape.xmax + 5, but_cursor_shape.ymin + 3, false);
2119       }
2120 
2121       /* composite underline */
2122       widget_draw_text_ime_underline(fstyle, wcol, but, rect, ime_data, drawstr);
2123     }
2124 #endif
2125   }
2126 
2127   if (fstyle->kerning == 1) {
2128     BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
2129   }
2130 
2131 #if 0
2132   ui_rasterpos_safe(x, y, but->aspect);
2133   transopts = ui_translate_buttons();
2134 #endif
2135 
2136   /* cut string in 2 parts - only for menu entries */
2137   if ((but->drawflag & UI_BUT_HAS_SHORTCUT) && (but->editstr == NULL)) {
2138     if (but->flag & UI_BUT_HAS_SEP_CHAR) {
2139       drawstr_right = strrchr(drawstr, UI_SEP_CHAR);
2140       if (drawstr_right) {
2141         drawstr_left_len = (drawstr_right - drawstr);
2142         drawstr_right++;
2143       }
2144     }
2145   }
2146 
2147 #ifdef USE_NUMBUTS_LR_ALIGN
2148   if (!drawstr_right && (but->drawflag & UI_BUT_TEXT_LEFT) &&
2149       ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER) &&
2150       /* if we're editing or multi-drag (fake editing), then use left alignment */
2151       (but->editstr == NULL) && (drawstr == but->drawstr)) {
2152     drawstr_right = strrchr(drawstr + but->ofs, ':');
2153     if (drawstr_right) {
2154       drawstr_right++;
2155       drawstr_left_len = (drawstr_right - drawstr - 1);
2156 
2157       while (*drawstr_right == ' ') {
2158         drawstr_right++;
2159       }
2160     }
2161     else {
2162       /* no prefix, even so use only cpoin */
2163       drawstr_right = drawstr + but->ofs;
2164       use_right_only = true;
2165     }
2166   }
2167 #endif
2168 
2169   if (!use_right_only) {
2170     /* for underline drawing */
2171     int font_xofs, font_yofs;
2172 
2173     int drawlen = (drawstr_left_len == INT_MAX) ? strlen(drawstr + but->ofs) :
2174                                                   (drawstr_left_len - but->ofs);
2175 
2176     if (drawlen > 0) {
2177       UI_fontstyle_draw_ex(fstyle,
2178                            rect,
2179                            drawstr + but->ofs,
2180                            wcol->text,
2181                            &(struct uiFontStyleDraw_Params){
2182                                .align = align,
2183                            },
2184                            drawlen,
2185                            &font_xofs,
2186                            &font_yofs,
2187                            NULL);
2188 
2189       if (but->menu_key != '\0') {
2190         const char *drawstr_ofs = drawstr + but->ofs;
2191         int ul_index = -1;
2192 
2193         {
2194           /* Find upper case, fallback to lower case. */
2195           const char *drawstr_end = drawstr_ofs + drawlen;
2196           const char keys[] = {but->menu_key - 32, but->menu_key};
2197           for (int i = 0; i < ARRAY_SIZE(keys); i++) {
2198             const char *drawstr_menu = strchr(drawstr_ofs, keys[i]);
2199             if (drawstr_menu != NULL && drawstr_menu < drawstr_end) {
2200               ul_index = (int)(drawstr_menu - drawstr_ofs);
2201               break;
2202             }
2203           }
2204         }
2205 
2206         if (ul_index != -1) {
2207           if (fstyle->kerning == 1) {
2208             BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
2209           }
2210 
2211           int ul_data[2] = {
2212               ul_index, /* Character index to test. */
2213               0,        /* Write the x-offset here. */
2214           };
2215           BLF_boundbox_foreach_glyph(fstyle->uifont_id,
2216                                      drawstr_ofs,
2217                                      ul_index + 1,
2218                                      widget_draw_text_underline_calc_center_x,
2219                                      ul_data);
2220           ul_data[1] -= BLF_width(fstyle->uifont_id, "_", 2) / 2.0f;
2221 
2222           BLF_position(fstyle->uifont_id,
2223                        rect->xmin + font_xofs + ul_data[1],
2224                        rect->ymin + font_yofs,
2225                        0.0f);
2226           BLF_color4ubv(fstyle->uifont_id, wcol->text);
2227           BLF_draw(fstyle->uifont_id, "_", 2);
2228 
2229           if (fstyle->kerning == 1) {
2230             BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
2231           }
2232         }
2233       }
2234     }
2235   }
2236 
2237   /* part text right aligned */
2238   if (drawstr_right) {
2239     uchar col[4];
2240     copy_v4_v4_uchar(col, wcol->text);
2241     if (but->drawflag & UI_BUT_HAS_SHORTCUT) {
2242       col[3] *= 0.5f;
2243     }
2244 
2245     rect->xmax -= UI_TEXT_CLIP_MARGIN;
2246     UI_fontstyle_draw(fstyle,
2247                       rect,
2248                       drawstr_right,
2249                       col,
2250                       &(struct uiFontStyleDraw_Params){
2251                           .align = UI_STYLE_TEXT_RIGHT,
2252                       });
2253   }
2254 }
2255 
widget_draw_extra_icons(const uiWidgetColors * wcol,uiBut * but,rcti * rect,float alpha)2256 static void widget_draw_extra_icons(const uiWidgetColors *wcol,
2257                                     uiBut *but,
2258                                     rcti *rect,
2259                                     float alpha)
2260 {
2261   /* inverse order, from right to left. */
2262   LISTBASE_FOREACH_BACKWARD (uiButExtraOpIcon *, op_icon, &but->extra_op_icons) {
2263     rcti temp = *rect;
2264     float alpha_this = alpha;
2265 
2266     temp.xmin = temp.xmax - (BLI_rcti_size_y(rect) * 1.08f);
2267 
2268     if (!op_icon->highlighted) {
2269       alpha_this *= 0.75f;
2270     }
2271 
2272     widget_draw_icon(but, op_icon->icon, alpha_this, &temp, wcol->text);
2273 
2274     rect->xmax -= ICON_SIZE_FROM_BUTRECT(rect);
2275   }
2276 }
2277 
widget_draw_node_link_socket(const uiWidgetColors * wcol,const rcti * rect,uiBut * but,float alpha)2278 static void widget_draw_node_link_socket(const uiWidgetColors *wcol,
2279                                          const rcti *rect,
2280                                          uiBut *but,
2281                                          float alpha)
2282 {
2283   /* Node socket pointer can be passed as custom_data, see UI_but_node_link_set(). */
2284   if (but->custom_data) {
2285     const float scale = 0.9f / but->block->aspect;
2286 
2287     float col[4];
2288     rgba_uchar_to_float(col, but->col);
2289     col[3] *= alpha;
2290 
2291     GPU_blend(GPU_BLEND_ALPHA);
2292     UI_widgetbase_draw_cache_flush();
2293     GPU_blend(GPU_BLEND_NONE);
2294 
2295     ED_node_socket_draw(but->custom_data, rect, col, scale);
2296   }
2297   else {
2298     widget_draw_icon(but, ICON_LAYER_USED, alpha, rect, wcol->text);
2299   }
2300 }
2301 
2302 /* draws text and icons for buttons */
widget_draw_text_icon(const uiFontStyle * fstyle,const uiWidgetColors * wcol,uiBut * but,rcti * rect)2303 static void widget_draw_text_icon(const uiFontStyle *fstyle,
2304                                   const uiWidgetColors *wcol,
2305                                   uiBut *but,
2306                                   rcti *rect)
2307 {
2308   const bool show_menu_icon = ui_but_draw_menu_icon(but);
2309   const float alpha = (float)wcol->text[3] / 255.0f;
2310   char password_str[UI_MAX_DRAW_STR];
2311   bool no_text_padding = but->drawflag & UI_BUT_NO_TEXT_PADDING;
2312 
2313   ui_but_text_password_hide(password_str, but, false);
2314 
2315   /* check for button text label */
2316   if (ELEM(but->type, UI_BTYPE_MENU, UI_BTYPE_POPOVER) && (but->flag & UI_BUT_NODE_LINK)) {
2317     rcti temp = *rect;
2318     const int size = BLI_rcti_size_y(rect) + 1; /* Not the icon size! */
2319 
2320     if (but->drawflag & UI_BUT_ICON_LEFT) {
2321       temp.xmax = rect->xmin + size;
2322       rect->xmin = temp.xmax;
2323       /* Further padding looks off. */
2324       no_text_padding = true;
2325     }
2326     else {
2327       temp.xmin = rect->xmax - size;
2328       rect->xmax = temp.xmin;
2329     }
2330 
2331     widget_draw_node_link_socket(wcol, &temp, but, alpha);
2332   }
2333 
2334   /* If there's an icon too (made with uiDefIconTextBut) then draw the icon
2335    * and offset the text label to accommodate it */
2336 
2337   /* Big previews with optional text label below */
2338   if (but->flag & UI_BUT_ICON_PREVIEW && ui_block_is_menu(but->block)) {
2339     const BIFIconID icon = ui_but_icon(but);
2340     int icon_size = BLI_rcti_size_y(rect);
2341     int text_size = 0;
2342 
2343     /* This is a bit britle, but avoids adding an 'UI_BUT_HAS_LABEL' flag to but... */
2344     if (icon_size > BLI_rcti_size_x(rect)) {
2345       /* button is not square, it has extra height for label */
2346       text_size = UI_UNIT_Y;
2347       icon_size -= text_size;
2348     }
2349 
2350     /* draw icon in rect above the space reserved for the label */
2351     rect->ymin += text_size;
2352     GPU_blend(GPU_BLEND_ALPHA);
2353     widget_draw_preview(icon, alpha, rect);
2354     GPU_blend(GPU_BLEND_NONE);
2355 
2356     /* offset rect to draw label in */
2357     rect->ymin -= text_size;
2358     rect->ymax -= icon_size;
2359 
2360     /* vertically centering text */
2361     rect->ymin += UI_UNIT_Y / 2;
2362   }
2363   /* Icons on the left with optional text label on the right */
2364   else if (but->flag & UI_HAS_ICON || show_menu_icon) {
2365     const bool is_tool = ((but->icon != ICON_NONE) & UI_but_is_tool(but));
2366 
2367     /* XXX add way to draw icons at a different size!
2368      * Use small icons for popup. */
2369 #ifdef USE_UI_TOOLBAR_HACK
2370     const float aspect_orig = but->block->aspect;
2371     if (is_tool && (but->block->flag & UI_BLOCK_POPOVER)) {
2372       but->block->aspect *= 2.0f;
2373     }
2374 #endif
2375 
2376     const BIFIconID icon = ui_but_icon(but);
2377     const int icon_size_init = is_tool ? ICON_DEFAULT_HEIGHT_TOOLBAR : ICON_DEFAULT_HEIGHT;
2378     const float icon_size = icon_size_init / (but->block->aspect * U.inv_dpi_fac);
2379     const float icon_padding = 2 * UI_DPI_FAC;
2380 
2381 #ifdef USE_UI_TOOLBAR_HACK
2382     if (is_tool) {
2383       /* pass (even if its a menu toolbar) */
2384       but->drawflag |= UI_BUT_TEXT_LEFT;
2385       but->drawflag |= UI_BUT_ICON_LEFT;
2386     }
2387 #endif
2388 
2389     /* menu item - add some more padding so menus don't feel cramped. it must
2390      * be part of the button so that this area is still clickable */
2391     if (is_tool) {
2392       /* pass (even if its a menu toolbar) */
2393     }
2394     else if (ui_block_is_pie_menu(but->block)) {
2395       if (but->emboss == UI_EMBOSS_RADIAL) {
2396         rect->xmin += 0.3f * U.widget_unit;
2397       }
2398     }
2399     /* Menu items, but only if they are not icon-only (rare). */
2400     else if (ui_block_is_menu(but->block) && but->drawstr[0]) {
2401       rect->xmin += 0.2f * U.widget_unit;
2402     }
2403 
2404     widget_draw_icon(but, icon, alpha, rect, wcol->text);
2405     if (show_menu_icon) {
2406       BLI_assert(but->block->content_hints & UI_BLOCK_CONTAINS_SUBMENU_BUT);
2407       widget_draw_submenu_tria(but, rect, wcol);
2408     }
2409 
2410 #ifdef USE_UI_TOOLBAR_HACK
2411     but->block->aspect = aspect_orig;
2412 #endif
2413 
2414     rect->xmin += icon_size + icon_padding;
2415   }
2416 
2417   if (!no_text_padding) {
2418     int text_padding = (UI_TEXT_MARGIN_X * U.widget_unit) / but->block->aspect;
2419     if (but->editstr) {
2420       rect->xmin += text_padding;
2421     }
2422     else if (but->flag & UI_BUT_DRAG_MULTI) {
2423       const bool text_is_edited = ui_but_drag_multi_edit_get(but) != NULL;
2424       if (text_is_edited || (but->drawflag & UI_BUT_TEXT_LEFT)) {
2425         rect->xmin += text_padding;
2426       }
2427     }
2428     else if (but->drawflag & UI_BUT_TEXT_LEFT) {
2429 
2430       /* Reduce the left padding for labels without an icon. */
2431       if ((but->type == UI_BTYPE_LABEL) && !(but->flag & UI_HAS_ICON) &&
2432           !ui_block_is_menu(but->block)) {
2433         text_padding /= 2;
2434       }
2435 
2436       rect->xmin += text_padding;
2437     }
2438     else if (but->drawflag & UI_BUT_TEXT_RIGHT) {
2439       rect->xmax -= text_padding;
2440     }
2441   }
2442 
2443   /* Menu contains sub-menu items with triangle icon on their right. Shortcut
2444    * strings should be drawn with some padding to the right then. */
2445   if (ui_block_is_menu(but->block) &&
2446       (but->block->content_hints & UI_BLOCK_CONTAINS_SUBMENU_BUT)) {
2447     rect->xmax -= UI_MENU_SUBMENU_PADDING;
2448   }
2449 
2450   /* extra icons, e.g. 'x' icon to clear text or icon for eyedropper */
2451   widget_draw_extra_icons(wcol, but, rect, alpha);
2452 
2453   /* clip but->drawstr to fit in available space */
2454   if (but->editstr && but->pos >= 0) {
2455     ui_text_clip_cursor(fstyle, but, rect);
2456   }
2457   else if (but->drawstr[0] == '\0') {
2458     /* bypass text clipping on icon buttons */
2459     but->ofs = 0;
2460     but->strwidth = 0;
2461   }
2462   else if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER)) {
2463     ui_text_clip_right_label(fstyle, but, rect);
2464   }
2465   else if (but->flag & UI_BUT_HAS_SEP_CHAR) {
2466     /* Clip middle, but protect in all case right part containing the shortcut, if any. */
2467     ui_text_clip_middle_protect_right(fstyle, but, rect, UI_SEP_CHAR);
2468   }
2469   else {
2470     ui_text_clip_middle(fstyle, but, rect);
2471   }
2472 
2473   /* always draw text for textbutton cursor */
2474   widget_draw_text(fstyle, wcol, but, rect);
2475 
2476   ui_but_text_password_hide(password_str, but, true);
2477 
2478   /* if a widget uses font shadow it has to be deactivated now */
2479   BLF_disable(fstyle->uifont_id, BLF_SHADOW);
2480 }
2481 
2482 #undef UI_TEXT_CLIP_MARGIN
2483 
2484 /** \} */
2485 
2486 /* -------------------------------------------------------------------- */
2487 /** \name Widget State Management
2488  *
2489  * Adjust widget display based on animated, driven, overridden ... etc.
2490  * \{ */
2491 
2492 /* put all widget colors on half alpha, use local storage */
ui_widget_color_disabled(uiWidgetType * wt,const int state)2493 static void ui_widget_color_disabled(uiWidgetType *wt, const int state)
2494 {
2495   static uiWidgetColors wcol_theme_s;
2496 
2497   wcol_theme_s = *wt->wcol_theme;
2498 
2499   const float factor = widget_alpha_factor(state);
2500 
2501   wcol_theme_s.outline[3] *= factor;
2502   wcol_theme_s.inner[3] *= factor;
2503   wcol_theme_s.inner_sel[3] *= factor;
2504   wcol_theme_s.item[3] *= factor;
2505   wcol_theme_s.text[3] *= factor;
2506   wcol_theme_s.text_sel[3] *= factor;
2507 
2508   wt->wcol_theme = &wcol_theme_s;
2509 }
2510 
widget_active_color(uiWidgetColors * wcol)2511 static void widget_active_color(uiWidgetColors *wcol)
2512 {
2513   const bool dark = (rgb_to_grayscale_byte(wcol->text) > rgb_to_grayscale_byte(wcol->inner));
2514   color_mul_hsl_v3(wcol->inner, 1.0f, 1.15f, dark ? 1.2f : 1.1f);
2515   color_mul_hsl_v3(wcol->outline, 1.0f, 1.15f, 1.15f);
2516   color_mul_hsl_v3(wcol->text, 1.0f, 1.15f, dark ? 1.25f : 0.8f);
2517 }
2518 
widget_color_blend_from_flags(const uiWidgetStateColors * wcol_state,int state,int drawflag)2519 static const uchar *widget_color_blend_from_flags(const uiWidgetStateColors *wcol_state,
2520                                                   int state,
2521                                                   int drawflag)
2522 {
2523   if (drawflag & UI_BUT_ANIMATED_CHANGED) {
2524     return wcol_state->inner_changed_sel;
2525   }
2526   if (state & UI_BUT_ANIMATED_KEY) {
2527     return wcol_state->inner_key_sel;
2528   }
2529   if (state & UI_BUT_ANIMATED) {
2530     return wcol_state->inner_anim_sel;
2531   }
2532   if (state & UI_BUT_DRIVEN) {
2533     return wcol_state->inner_driven_sel;
2534   }
2535   if (state & UI_BUT_OVERRIDEN) {
2536     return wcol_state->inner_overridden_sel;
2537   }
2538   return NULL;
2539 }
2540 
2541 /* copy colors from theme, and set changes in it based on state */
widget_state(uiWidgetType * wt,int state,int drawflag)2542 static void widget_state(uiWidgetType *wt, int state, int drawflag)
2543 {
2544   uiWidgetStateColors *wcol_state = wt->wcol_state;
2545 
2546   if ((state & UI_BUT_LIST_ITEM) && !(state & UI_STATE_TEXT_INPUT)) {
2547     /* Override default widget's colors. */
2548     bTheme *btheme = UI_GetTheme();
2549     wt->wcol_theme = &btheme->tui.wcol_list_item;
2550 
2551     if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_SEARCH_FILTER_NO_MATCH)) {
2552       ui_widget_color_disabled(wt, state & UI_SEARCH_FILTER_NO_MATCH);
2553     }
2554   }
2555 
2556   wt->wcol = *(wt->wcol_theme);
2557 
2558   const uchar *color_blend = widget_color_blend_from_flags(wcol_state, state, drawflag);
2559 
2560   if (state & UI_SELECT) {
2561     copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel);
2562     if (color_blend != NULL) {
2563       color_blend_v3_v3(wt->wcol.inner, color_blend, wcol_state->blend);
2564     }
2565 
2566     copy_v3_v3_uchar(wt->wcol.text, wt->wcol.text_sel);
2567 
2568     if (state & UI_SELECT) {
2569       SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown);
2570     }
2571   }
2572   else {
2573     if (state & UI_BUT_ACTIVE_DEFAULT) {
2574       copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel);
2575       copy_v4_v4_uchar(wt->wcol.text, wt->wcol.text_sel);
2576     }
2577     if (color_blend != NULL) {
2578       color_blend_v3_v3(wt->wcol.inner, color_blend, wcol_state->blend);
2579     }
2580 
2581     /* Add "hover" highlight. Ideally this could apply in all cases,
2582      * even if UI_SELECT. But currently this causes some flickering
2583      * as buttons can be created and updated without respect to mouse
2584      * position and so can draw without UI_ACTIVE set.  See D6503. */
2585     if (state & UI_ACTIVE) {
2586       widget_active_color(&wt->wcol);
2587     }
2588   }
2589 
2590   if (state & UI_BUT_REDALERT) {
2591     const uchar red[4] = {255, 0, 0};
2592     if (wt->draw) {
2593       color_blend_v3_v3(wt->wcol.inner, red, 0.4f);
2594     }
2595     else {
2596       color_blend_v3_v3(wt->wcol.text, red, 0.4f);
2597     }
2598   }
2599 
2600   if (state & UI_BUT_DRAG_MULTI) {
2601     /* the button isn't SELECT but we're editing this so draw with sel color */
2602     copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel);
2603     SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown);
2604     color_blend_v3_v3(wt->wcol.text, wt->wcol.text_sel, 0.85f);
2605   }
2606 
2607   if (state & UI_BUT_NODE_ACTIVE) {
2608     const uchar blue[4] = {86, 128, 194};
2609     color_blend_v3_v3(wt->wcol.inner, blue, 0.3f);
2610   }
2611 }
2612 
2613 /** \} */
2614 
2615 /* -------------------------------------------------------------------- */
2616 /** \name Widget Types
2617  * \{ */
2618 
2619 /* sliders use special hack which sets 'item' as inner when drawing filling */
widget_state_numslider(uiWidgetType * wt,int state,int drawflag)2620 static void widget_state_numslider(uiWidgetType *wt, int state, int drawflag)
2621 {
2622   uiWidgetStateColors *wcol_state = wt->wcol_state;
2623 
2624   /* call this for option button */
2625   widget_state(wt, state, drawflag);
2626 
2627   const uchar *color_blend = widget_color_blend_from_flags(wcol_state, state, drawflag);
2628   if (color_blend != NULL) {
2629     /* Set the slider 'item' so that it reflects state settings too.
2630      * De-saturate so the color of the slider doesn't conflict with the blend color,
2631      * which can make the color hard to see when the slider is set to full (see T66102). */
2632     wt->wcol.item[0] = wt->wcol.item[1] = wt->wcol.item[2] = rgb_to_grayscale_byte(wt->wcol.item);
2633     color_blend_v3_v3(wt->wcol.item, color_blend, wcol_state->blend);
2634     color_ensure_contrast_v3(wt->wcol.item, wt->wcol.inner, 30);
2635   }
2636 
2637   if (state & UI_SELECT) {
2638     SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown);
2639   }
2640 }
2641 
2642 /* labels use theme colors for text */
widget_state_option_menu(uiWidgetType * wt,int state,int drawflag)2643 static void widget_state_option_menu(uiWidgetType *wt, int state, int drawflag)
2644 {
2645   const bTheme *btheme = UI_GetTheme();
2646 
2647   const uiWidgetColors *old_wcol = wt->wcol_theme;
2648   uiWidgetColors wcol_menu_option = *wt->wcol_theme;
2649 
2650   /* Override the checkbox theme colors to use the menu-back text colors. */
2651   copy_v3_v3_uchar(wcol_menu_option.text, btheme->tui.wcol_menu_back.text);
2652   copy_v3_v3_uchar(wcol_menu_option.text_sel, btheme->tui.wcol_menu_back.text_sel);
2653   wt->wcol_theme = &wcol_menu_option;
2654 
2655   widget_state(wt, state, drawflag);
2656 
2657   wt->wcol_theme = old_wcol;
2658 }
2659 
widget_state_nothing(uiWidgetType * wt,int UNUSED (state),int UNUSED (drawflag))2660 static void widget_state_nothing(uiWidgetType *wt, int UNUSED(state), int UNUSED(drawflag))
2661 {
2662   wt->wcol = *(wt->wcol_theme);
2663 }
2664 
2665 /* special case, button that calls pulldown */
widget_state_pulldown(uiWidgetType * wt,int UNUSED (state),int UNUSED (drawflag))2666 static void widget_state_pulldown(uiWidgetType *wt, int UNUSED(state), int UNUSED(drawflag))
2667 {
2668   wt->wcol = *(wt->wcol_theme);
2669 }
2670 
2671 /* special case, pie menu items */
widget_state_pie_menu_item(uiWidgetType * wt,int state,int UNUSED (drawflag))2672 static void widget_state_pie_menu_item(uiWidgetType *wt, int state, int UNUSED(drawflag))
2673 {
2674   wt->wcol = *(wt->wcol_theme);
2675 
2676   /* active and disabled (not so common) */
2677   if ((state & UI_BUT_DISABLED) && (state & UI_ACTIVE)) {
2678     color_blend_v3_v3(wt->wcol.text, wt->wcol.text_sel, 0.5f);
2679     /* draw the backdrop at low alpha, helps navigating with keys
2680      * when disabled items are active */
2681     copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.item);
2682     wt->wcol.inner[3] = 64;
2683   }
2684   else {
2685     /* regular active */
2686     if (state & (UI_SELECT | UI_ACTIVE)) {
2687       copy_v3_v3_uchar(wt->wcol.text, wt->wcol.text_sel);
2688     }
2689     else if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) {
2690       /* regular disabled */
2691       color_blend_v3_v3(wt->wcol.text, wt->wcol.inner, 0.5f);
2692     }
2693 
2694     if (state & UI_SELECT) {
2695       copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel);
2696     }
2697     else if (state & UI_ACTIVE) {
2698       copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.item);
2699     }
2700   }
2701 }
2702 
2703 /* special case, menu items */
widget_state_menu_item(uiWidgetType * wt,int state,int UNUSED (drawflag))2704 static void widget_state_menu_item(uiWidgetType *wt, int state, int UNUSED(drawflag))
2705 {
2706   wt->wcol = *(wt->wcol_theme);
2707 
2708   /* active and disabled (not so common) */
2709   if ((state & UI_BUT_DISABLED) && (state & UI_ACTIVE)) {
2710     /* draw the backdrop at low alpha, helps navigating with keys
2711      * when disabled items are active */
2712     wt->wcol.text[3] = 128;
2713     color_blend_v3_v3(wt->wcol.inner, wt->wcol.text, 0.5f);
2714     wt->wcol.inner[3] = 64;
2715   }
2716   else {
2717     /* regular active */
2718     if (state & UI_ACTIVE) {
2719       copy_v3_v3_uchar(wt->wcol.text, wt->wcol.text_sel);
2720     }
2721     else if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) {
2722       /* regular disabled */
2723       color_blend_v3_v3(wt->wcol.text, wt->wcol.inner, 0.5f);
2724     }
2725 
2726     if (state & UI_ACTIVE) {
2727       copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel);
2728     }
2729   }
2730 }
2731 
2732 /** \} */
2733 
2734 /* -------------------------------------------------------------------- */
2735 /** \name Draw Menu Backdrop
2736  * \{ */
2737 
2738 /* outside of rect, rad to left/bottom/right */
widget_softshadow(const rcti * rect,int roundboxalign,const float radin)2739 static void widget_softshadow(const rcti *rect, int roundboxalign, const float radin)
2740 {
2741   bTheme *btheme = UI_GetTheme();
2742   uiWidgetBase wtb;
2743   rcti rect1 = *rect;
2744   float alphastep;
2745   int step, totvert;
2746   float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2];
2747   const float radout = UI_ThemeMenuShadowWidth();
2748 
2749   /* disabled shadow */
2750   if (radout == 0.0f) {
2751     return;
2752   }
2753 
2754   /* prevent tooltips to not show round shadow */
2755   if (radout > 0.2f * BLI_rcti_size_y(&rect1)) {
2756     rect1.ymax -= 0.2f * BLI_rcti_size_y(&rect1);
2757   }
2758   else {
2759     rect1.ymax -= radout;
2760   }
2761 
2762   /* inner part */
2763   totvert = round_box_shadow_edges(wtb.inner_v,
2764                                    &rect1,
2765                                    radin,
2766                                    roundboxalign & (UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT),
2767                                    0.0f);
2768 
2769   /* we draw a number of increasing size alpha quad strips */
2770   alphastep = 3.0f * btheme->tui.menu_shadow_fac / radout;
2771 
2772   const uint pos = GPU_vertformat_attr_add(
2773       immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
2774 
2775   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2776 
2777   for (step = 1; step <= (int)radout; step++) {
2778     const float expfac = sqrtf(step / radout);
2779 
2780     round_box_shadow_edges(wtb.outer_v, &rect1, radin, UI_CNR_ALL, (float)step);
2781 
2782     immUniformColor4f(0.0f, 0.0f, 0.0f, alphastep * (1.0f - expfac));
2783 
2784     widget_verts_to_triangle_strip(&wtb, totvert, triangle_strip);
2785 
2786     widget_draw_vertex_buffer(pos, 0, GPU_PRIM_TRI_STRIP, triangle_strip, NULL, totvert * 2);
2787   }
2788 
2789   immUnbindProgram();
2790 }
2791 
widget_menu_back(uiWidgetColors * wcol,rcti * rect,int flag,int direction)2792 static void widget_menu_back(uiWidgetColors *wcol, rcti *rect, int flag, int direction)
2793 {
2794   uiWidgetBase wtb;
2795   int roundboxalign = UI_CNR_ALL;
2796 
2797   widget_init(&wtb);
2798 
2799   /* menu is 2nd level or deeper */
2800   if (flag & UI_BLOCK_POPUP) {
2801     // rect->ymin -= 4.0;
2802     // rect->ymax += 4.0;
2803   }
2804   else if (direction == UI_DIR_DOWN) {
2805     roundboxalign = (UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT);
2806     rect->ymin -= 0.1f * U.widget_unit;
2807   }
2808   else if (direction == UI_DIR_UP) {
2809     roundboxalign = UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT;
2810     rect->ymax += 0.1f * U.widget_unit;
2811   }
2812 
2813   GPU_blend(GPU_BLEND_ALPHA);
2814   widget_softshadow(rect, roundboxalign, wcol->roundness * U.widget_unit);
2815 
2816   round_box_edges(&wtb, roundboxalign, rect, wcol->roundness * U.widget_unit);
2817   wtb.draw_emboss = false;
2818   widgetbase_draw(&wtb, wcol);
2819 
2820   GPU_blend(GPU_BLEND_NONE);
2821 }
2822 
ui_hsv_cursor(float x,float y)2823 static void ui_hsv_cursor(float x, float y)
2824 {
2825   const uint pos = GPU_vertformat_attr_add(
2826       immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
2827 
2828   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2829 
2830   immUniformColor3f(1.0f, 1.0f, 1.0f);
2831   imm_draw_circle_fill_2d(pos, x, y, 3.0f * U.pixelsize, 8);
2832 
2833   GPU_blend(GPU_BLEND_ALPHA);
2834   GPU_line_smooth(true);
2835   immUniformColor3f(0.0f, 0.0f, 0.0f);
2836   imm_draw_circle_wire_2d(pos, x, y, 3.0f * U.pixelsize, 12);
2837   GPU_blend(GPU_BLEND_NONE);
2838   GPU_line_smooth(false);
2839 
2840   immUnbindProgram();
2841 }
2842 
ui_hsvcircle_vals_from_pos(const rcti * rect,const float mx,const float my,float * r_val_rad,float * r_val_dist)2843 void ui_hsvcircle_vals_from_pos(
2844     const rcti *rect, const float mx, const float my, float *r_val_rad, float *r_val_dist)
2845 {
2846   /* duplication of code... well, simple is better now */
2847   const float centx = BLI_rcti_cent_x_fl(rect);
2848   const float centy = BLI_rcti_cent_y_fl(rect);
2849   const float radius = (float)min_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)) / 2.0f;
2850   const float m_delta[2] = {mx - centx, my - centy};
2851   const float dist_sq = len_squared_v2(m_delta);
2852 
2853   *r_val_dist = (dist_sq < (radius * radius)) ? sqrtf(dist_sq) / radius : 1.0f;
2854   *r_val_rad = atan2f(m_delta[0], m_delta[1]) / (2.0f * (float)M_PI) + 0.5f;
2855 }
2856 
2857 /* cursor in hsv circle, in float units -1 to 1, to map on radius */
ui_hsvcircle_pos_from_vals(const ColorPicker * cpicker,const rcti * rect,const float * hsv,float * r_xpos,float * r_ypos)2858 void ui_hsvcircle_pos_from_vals(
2859     const ColorPicker *cpicker, const rcti *rect, const float *hsv, float *r_xpos, float *r_ypos)
2860 {
2861   /* duplication of code... well, simple is better now */
2862   const float centx = BLI_rcti_cent_x_fl(rect);
2863   const float centy = BLI_rcti_cent_y_fl(rect);
2864   float radius = (float)min_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)) / 2.0f;
2865   float ang, radius_t;
2866 
2867   ang = 2.0f * (float)M_PI * hsv[0] + (float)M_PI_2;
2868 
2869   if (cpicker->use_color_cubic && (U.color_picker_type == USER_CP_CIRCLE_HSV)) {
2870     radius_t = (1.0f - pow3f(1.0f - hsv[1]));
2871   }
2872   else {
2873     radius_t = hsv[1];
2874   }
2875 
2876   radius = clamp_f(radius_t, 0.0f, 1.0f) * radius;
2877   *r_xpos = centx + cosf(-ang) * radius;
2878   *r_ypos = centy + sinf(-ang) * radius;
2879 }
2880 
ui_draw_but_HSVCIRCLE(uiBut * but,const uiWidgetColors * wcol,const rcti * rect)2881 static void ui_draw_but_HSVCIRCLE(uiBut *but, const uiWidgetColors *wcol, const rcti *rect)
2882 {
2883   /* TODO(merwin): reimplement as shader for pixel-perfect colors */
2884 
2885   const int tot = 64;
2886   const float radstep = 2.0f * (float)M_PI / (float)tot;
2887   const float centx = BLI_rcti_cent_x_fl(rect);
2888   const float centy = BLI_rcti_cent_y_fl(rect);
2889   const float radius = (float)min_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)) / 2.0f;
2890 
2891   ColorPicker *cpicker = but->custom_data;
2892   float rgb[3], hsv[3], rgb_center[3];
2893   const bool is_color_gamma = ui_but_is_color_gamma(but);
2894 
2895   /* Initialize for compatibility. */
2896   copy_v3_v3(hsv, cpicker->color_data);
2897 
2898   /* Compute current hue. */
2899   ui_but_v3_get(but, rgb);
2900   ui_scene_linear_to_color_picker_space(but, rgb);
2901   ui_rgb_to_color_picker_compat_v(rgb, hsv);
2902 
2903   CLAMP(hsv[2], 0.0f, 1.0f); /* for display only */
2904 
2905   /* exception: if 'lock' is set
2906    * lock the value of the color wheel to 1.
2907    * Useful for color correction tools where you're only interested in hue. */
2908   if (cpicker->use_color_lock) {
2909     if (U.color_picker_type == USER_CP_CIRCLE_HSV) {
2910       hsv[2] = 1.0f;
2911     }
2912     else {
2913       hsv[2] = 0.5f;
2914     }
2915   }
2916 
2917   const float hsv_center[3] = {0.0f, 0.0f, hsv[2]};
2918   ui_color_picker_to_rgb_v(hsv_center, rgb_center);
2919   ui_color_picker_to_scene_linear_space(but, rgb_center);
2920 
2921   if (!is_color_gamma) {
2922     ui_block_cm_to_display_space_v3(but->block, rgb_center);
2923   }
2924 
2925   GPUVertFormat *format = immVertexFormat();
2926   uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
2927   const uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
2928 
2929   immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
2930 
2931   immBegin(GPU_PRIM_TRI_FAN, tot + 2);
2932   immAttr3fv(color, rgb_center);
2933   immVertex2f(pos, centx, centy);
2934 
2935   float ang = 0.0f;
2936   for (int a = 0; a <= tot; a++, ang += radstep) {
2937     const float si = sinf(ang);
2938     const float co = cosf(ang);
2939     float hsv_ang[3];
2940     float rgb_ang[3];
2941 
2942     ui_hsvcircle_vals_from_pos(
2943         rect, centx + co * radius, centy + si * radius, hsv_ang, hsv_ang + 1);
2944     hsv_ang[2] = hsv[2];
2945 
2946     ui_color_picker_to_rgb_v(hsv_ang, rgb_ang);
2947     ui_color_picker_to_scene_linear_space(but, rgb_ang);
2948 
2949     if (!is_color_gamma) {
2950       ui_block_cm_to_display_space_v3(but->block, rgb_ang);
2951     }
2952 
2953     immAttr3fv(color, rgb_ang);
2954     immVertex2f(pos, centx + co * radius, centy + si * radius);
2955   }
2956   immEnd();
2957   immUnbindProgram();
2958 
2959   /* fully rounded outline */
2960   format = immVertexFormat();
2961   pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
2962 
2963   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2964 
2965   GPU_blend(GPU_BLEND_ALPHA);
2966   GPU_line_smooth(true);
2967 
2968   immUniformColor3ubv(wcol->outline);
2969   imm_draw_circle_wire_2d(pos, centx, centy, radius, tot);
2970 
2971   immUnbindProgram();
2972 
2973   GPU_blend(GPU_BLEND_NONE);
2974   GPU_line_smooth(false);
2975 
2976   /* cursor */
2977   copy_v3_v3(hsv, cpicker->color_data);
2978   ui_but_v3_get(but, rgb);
2979   ui_scene_linear_to_color_picker_space(but, rgb);
2980   ui_rgb_to_color_picker_compat_v(rgb, hsv);
2981 
2982   float xpos, ypos;
2983   ui_hsvcircle_pos_from_vals(cpicker, rect, hsv, &xpos, &ypos);
2984   ui_hsv_cursor(xpos, ypos);
2985 }
2986 
2987 /** \} */
2988 
2989 /* -------------------------------------------------------------------- */
2990 /** \name Draw Custom Buttons
2991  * \{ */
2992 
2993 /* draws in resolution of 48x4 colors */
ui_draw_gradient(const rcti * rect,const float hsv[3],const eButGradientType type,const float alpha)2994 void ui_draw_gradient(const rcti *rect,
2995                       const float hsv[3],
2996                       const eButGradientType type,
2997                       const float alpha)
2998 {
2999   /* allows for 4 steps (red->yellow) */
3000   const int steps = 48;
3001   const float color_step = 1.0f / steps;
3002   int a;
3003   const float h = hsv[0], s = hsv[1], v = hsv[2];
3004   float dx, dy, sx1, sx2, sy;
3005   float col0[4][3]; /* left half, rect bottom to top */
3006   float col1[4][3]; /* right half, rect bottom to top */
3007 
3008   /* draw series of gouraud rects */
3009 
3010   switch (type) {
3011     case UI_GRAD_SV:
3012       hsv_to_rgb(h, 0.0, 0.0, &col1[0][0], &col1[0][1], &col1[0][2]);
3013       hsv_to_rgb(h, 0.0, 0.333, &col1[1][0], &col1[1][1], &col1[1][2]);
3014       hsv_to_rgb(h, 0.0, 0.666, &col1[2][0], &col1[2][1], &col1[2][2]);
3015       hsv_to_rgb(h, 0.0, 1.0, &col1[3][0], &col1[3][1], &col1[3][2]);
3016       break;
3017     case UI_GRAD_HV:
3018       hsv_to_rgb(0.0, s, 0.0, &col1[0][0], &col1[0][1], &col1[0][2]);
3019       hsv_to_rgb(0.0, s, 0.333, &col1[1][0], &col1[1][1], &col1[1][2]);
3020       hsv_to_rgb(0.0, s, 0.666, &col1[2][0], &col1[2][1], &col1[2][2]);
3021       hsv_to_rgb(0.0, s, 1.0, &col1[3][0], &col1[3][1], &col1[3][2]);
3022       break;
3023     case UI_GRAD_HS:
3024       hsv_to_rgb(0.0, 0.0, v, &col1[0][0], &col1[0][1], &col1[0][2]);
3025       hsv_to_rgb(0.0, 0.333, v, &col1[1][0], &col1[1][1], &col1[1][2]);
3026       hsv_to_rgb(0.0, 0.666, v, &col1[2][0], &col1[2][1], &col1[2][2]);
3027       hsv_to_rgb(0.0, 1.0, v, &col1[3][0], &col1[3][1], &col1[3][2]);
3028       break;
3029     case UI_GRAD_H:
3030       hsv_to_rgb(0.0, 1.0, 1.0, &col1[0][0], &col1[0][1], &col1[0][2]);
3031       copy_v3_v3(col1[1], col1[0]);
3032       copy_v3_v3(col1[2], col1[0]);
3033       copy_v3_v3(col1[3], col1[0]);
3034       break;
3035     case UI_GRAD_S:
3036       hsv_to_rgb(1.0, 0.0, 1.0, &col1[1][0], &col1[1][1], &col1[1][2]);
3037       copy_v3_v3(col1[0], col1[1]);
3038       copy_v3_v3(col1[2], col1[1]);
3039       copy_v3_v3(col1[3], col1[1]);
3040       break;
3041     case UI_GRAD_V:
3042       hsv_to_rgb(1.0, 1.0, 0.0, &col1[2][0], &col1[2][1], &col1[2][2]);
3043       copy_v3_v3(col1[0], col1[2]);
3044       copy_v3_v3(col1[1], col1[2]);
3045       copy_v3_v3(col1[3], col1[2]);
3046       break;
3047     default:
3048       BLI_assert(!"invalid 'type' argument");
3049       hsv_to_rgb(1.0, 1.0, 1.0, &col1[2][0], &col1[2][1], &col1[2][2]);
3050       copy_v3_v3(col1[0], col1[2]);
3051       copy_v3_v3(col1[1], col1[2]);
3052       copy_v3_v3(col1[3], col1[2]);
3053       break;
3054   }
3055 
3056   /* old below */
3057   GPUVertFormat *format = immVertexFormat();
3058   const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
3059   const uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
3060   immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
3061 
3062   immBegin(GPU_PRIM_TRIS, steps * 3 * 6);
3063 
3064   /* 0.999 = prevent float inaccuracy for steps */
3065   for (dx = 0.0f; dx < 0.999f; dx += color_step) {
3066     const float dx_next = dx + color_step;
3067 
3068     /* previous color */
3069     copy_v3_v3(col0[0], col1[0]);
3070     copy_v3_v3(col0[1], col1[1]);
3071     copy_v3_v3(col0[2], col1[2]);
3072     copy_v3_v3(col0[3], col1[3]);
3073 
3074     /* new color */
3075     switch (type) {
3076       case UI_GRAD_SV:
3077         hsv_to_rgb(h, dx, 0.0, &col1[0][0], &col1[0][1], &col1[0][2]);
3078         hsv_to_rgb(h, dx, 0.333, &col1[1][0], &col1[1][1], &col1[1][2]);
3079         hsv_to_rgb(h, dx, 0.666, &col1[2][0], &col1[2][1], &col1[2][2]);
3080         hsv_to_rgb(h, dx, 1.0, &col1[3][0], &col1[3][1], &col1[3][2]);
3081         break;
3082       case UI_GRAD_HV:
3083         hsv_to_rgb(dx_next, s, 0.0, &col1[0][0], &col1[0][1], &col1[0][2]);
3084         hsv_to_rgb(dx_next, s, 0.333, &col1[1][0], &col1[1][1], &col1[1][2]);
3085         hsv_to_rgb(dx_next, s, 0.666, &col1[2][0], &col1[2][1], &col1[2][2]);
3086         hsv_to_rgb(dx_next, s, 1.0, &col1[3][0], &col1[3][1], &col1[3][2]);
3087         break;
3088       case UI_GRAD_HS:
3089         hsv_to_rgb(dx_next, 0.0, v, &col1[0][0], &col1[0][1], &col1[0][2]);
3090         hsv_to_rgb(dx_next, 0.333, v, &col1[1][0], &col1[1][1], &col1[1][2]);
3091         hsv_to_rgb(dx_next, 0.666, v, &col1[2][0], &col1[2][1], &col1[2][2]);
3092         hsv_to_rgb(dx_next, 1.0, v, &col1[3][0], &col1[3][1], &col1[3][2]);
3093         break;
3094       case UI_GRAD_H:
3095         /* annoying but without this the color shifts - could be solved some other way
3096          * - campbell */
3097         hsv_to_rgb(dx_next, 1.0, 1.0, &col1[0][0], &col1[0][1], &col1[0][2]);
3098         copy_v3_v3(col1[1], col1[0]);
3099         copy_v3_v3(col1[2], col1[0]);
3100         copy_v3_v3(col1[3], col1[0]);
3101         break;
3102       case UI_GRAD_S:
3103         hsv_to_rgb(h, dx, 1.0, &col1[1][0], &col1[1][1], &col1[1][2]);
3104         copy_v3_v3(col1[0], col1[1]);
3105         copy_v3_v3(col1[2], col1[1]);
3106         copy_v3_v3(col1[3], col1[1]);
3107         break;
3108       case UI_GRAD_V:
3109         hsv_to_rgb(h, 1.0, dx, &col1[2][0], &col1[2][1], &col1[2][2]);
3110         copy_v3_v3(col1[0], col1[2]);
3111         copy_v3_v3(col1[1], col1[2]);
3112         copy_v3_v3(col1[3], col1[2]);
3113         break;
3114       default:
3115         break;
3116     }
3117 
3118     /* rect */
3119     sx1 = rect->xmin + dx * BLI_rcti_size_x(rect);
3120     sx2 = rect->xmin + dx_next * BLI_rcti_size_x(rect);
3121     sy = rect->ymin;
3122     dy = (float)BLI_rcti_size_y(rect) / 3.0f;
3123 
3124     for (a = 0; a < 3; a++, sy += dy) {
3125       immAttr4f(col, col0[a][0], col0[a][1], col0[a][2], alpha);
3126       immVertex2f(pos, sx1, sy);
3127 
3128       immAttr4f(col, col1[a][0], col1[a][1], col1[a][2], alpha);
3129       immVertex2f(pos, sx2, sy);
3130 
3131       immAttr4f(col, col1[a + 1][0], col1[a + 1][1], col1[a + 1][2], alpha);
3132       immVertex2f(pos, sx2, sy + dy);
3133 
3134       immAttr4f(col, col0[a][0], col0[a][1], col0[a][2], alpha);
3135       immVertex2f(pos, sx1, sy);
3136 
3137       immAttr4f(col, col1[a + 1][0], col1[a + 1][1], col1[a + 1][2], alpha);
3138       immVertex2f(pos, sx2, sy + dy);
3139 
3140       immAttr4f(col, col0[a + 1][0], col0[a + 1][1], col0[a + 1][2], alpha);
3141       immVertex2f(pos, sx1, sy + dy);
3142     }
3143   }
3144   immEnd();
3145 
3146   immUnbindProgram();
3147 }
3148 
ui_hsvcube_pos_from_vals(const uiButHSVCube * hsv_but,const rcti * rect,const float * hsv,float * r_xp,float * r_yp)3149 void ui_hsvcube_pos_from_vals(
3150     const uiButHSVCube *hsv_but, const rcti *rect, const float *hsv, float *r_xp, float *r_yp)
3151 {
3152   float x = 0.0f, y = 0.0f;
3153 
3154   switch (hsv_but->gradient_type) {
3155     case UI_GRAD_SV:
3156       x = hsv[1];
3157       y = hsv[2];
3158       break;
3159     case UI_GRAD_HV:
3160       x = hsv[0];
3161       y = hsv[2];
3162       break;
3163     case UI_GRAD_HS:
3164       x = hsv[0];
3165       y = hsv[1];
3166       break;
3167     case UI_GRAD_H:
3168       x = hsv[0];
3169       y = 0.5;
3170       break;
3171     case UI_GRAD_S:
3172       x = hsv[1];
3173       y = 0.5;
3174       break;
3175     case UI_GRAD_V:
3176       x = hsv[2];
3177       y = 0.5;
3178       break;
3179     case UI_GRAD_L_ALT:
3180       x = 0.5f;
3181       /* exception only for value strip - use the range set in but->min/max */
3182       y = hsv[2];
3183       break;
3184     case UI_GRAD_V_ALT:
3185       x = 0.5f;
3186       /* exception only for value strip - use the range set in but->min/max */
3187       y = (hsv[2] - hsv_but->but.softmin) / (hsv_but->but.softmax - hsv_but->but.softmin);
3188       break;
3189   }
3190 
3191   /* cursor */
3192   *r_xp = rect->xmin + x * BLI_rcti_size_x(rect);
3193   *r_yp = rect->ymin + y * BLI_rcti_size_y(rect);
3194 }
3195 
ui_draw_but_HSVCUBE(uiBut * but,const rcti * rect)3196 static void ui_draw_but_HSVCUBE(uiBut *but, const rcti *rect)
3197 {
3198   const uiButHSVCube *hsv_but = (uiButHSVCube *)but;
3199   float rgb[3];
3200   float x = 0.0f, y = 0.0f;
3201   ColorPicker *cpicker = but->custom_data;
3202   float *hsv = cpicker->color_data;
3203   float hsv_n[3];
3204 
3205   /* Initialize for compatibility. */
3206   copy_v3_v3(hsv_n, hsv);
3207 
3208   ui_but_v3_get(but, rgb);
3209   ui_scene_linear_to_color_picker_space(but, rgb);
3210   rgb_to_hsv_compat_v(rgb, hsv_n);
3211 
3212   ui_draw_gradient(rect, hsv_n, hsv_but->gradient_type, 1.0f);
3213 
3214   ui_hsvcube_pos_from_vals(hsv_but, rect, hsv_n, &x, &y);
3215   CLAMP(x, rect->xmin + 3.0f, rect->xmax - 3.0f);
3216   CLAMP(y, rect->ymin + 3.0f, rect->ymax - 3.0f);
3217 
3218   ui_hsv_cursor(x, y);
3219 
3220   /* outline */
3221   const uint pos = GPU_vertformat_attr_add(
3222       immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
3223   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
3224   immUniformColor3ub(0, 0, 0);
3225   imm_draw_box_wire_2d(pos, (rect->xmin), (rect->ymin), (rect->xmax), (rect->ymax));
3226   immUnbindProgram();
3227 }
3228 
3229 /* vertical 'value' slider, using new widget code */
ui_draw_but_HSV_v(uiBut * but,const rcti * rect)3230 static void ui_draw_but_HSV_v(uiBut *but, const rcti *rect)
3231 {
3232   const uiButHSVCube *hsv_but = (uiButHSVCube *)but;
3233   bTheme *btheme = UI_GetTheme();
3234   uiWidgetColors *wcol = &btheme->tui.wcol_numslider;
3235   uiWidgetBase wtb;
3236   const float rad = wcol->roundness * BLI_rcti_size_x(rect);
3237   float x, y;
3238   float rgb[3], hsv[3], v;
3239 
3240   ui_but_v3_get(but, rgb);
3241   ui_scene_linear_to_color_picker_space(but, rgb);
3242 
3243   if (hsv_but->gradient_type == UI_GRAD_L_ALT) {
3244     rgb_to_hsl_v(rgb, hsv);
3245   }
3246   else {
3247     rgb_to_hsv_v(rgb, hsv);
3248   }
3249   v = hsv[2];
3250 
3251   /* map v from property range to [0,1] */
3252   if (hsv_but->gradient_type == UI_GRAD_V_ALT) {
3253     const float min = but->softmin, max = but->softmax;
3254     v = (v - min) / (max - min);
3255   }
3256 
3257   widget_init(&wtb);
3258 
3259   /* fully rounded */
3260   round_box_edges(&wtb, UI_CNR_ALL, rect, rad);
3261 
3262   /* setup temp colors */
3263   widgetbase_draw(&wtb,
3264                   &((uiWidgetColors){
3265                       .outline = {0, 0, 0, 255},
3266                       .inner = {128, 128, 128, 255},
3267                       .shadetop = 127,
3268                       .shadedown = -128,
3269                       .shaded = 1,
3270                   }));
3271 
3272   /* We are drawing on top of widget bases. Flush cache. */
3273   GPU_blend(GPU_BLEND_ALPHA);
3274   UI_widgetbase_draw_cache_flush();
3275   GPU_blend(GPU_BLEND_NONE);
3276 
3277   /* cursor */
3278   x = rect->xmin + 0.5f * BLI_rcti_size_x(rect);
3279   y = rect->ymin + v * BLI_rcti_size_y(rect);
3280   CLAMP(y, rect->ymin + 3.0f, rect->ymax - 3.0f);
3281 
3282   ui_hsv_cursor(x, y);
3283 }
3284 
3285 /** Separator for menus. */
ui_draw_separator(const rcti * rect,const uiWidgetColors * wcol)3286 static void ui_draw_separator(const rcti *rect, const uiWidgetColors *wcol)
3287 {
3288   const int y = rect->ymin + BLI_rcti_size_y(rect) / 2 - 1;
3289   const uchar col[4] = {
3290       wcol->text[0],
3291       wcol->text[1],
3292       wcol->text[2],
3293       30,
3294   };
3295 
3296   const uint pos = GPU_vertformat_attr_add(
3297       immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
3298   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
3299 
3300   GPU_blend(GPU_BLEND_ALPHA);
3301   immUniformColor4ubv(col);
3302   GPU_line_width(1.0f);
3303 
3304   immBegin(GPU_PRIM_LINES, 2);
3305   immVertex2f(pos, rect->xmin, y);
3306   immVertex2f(pos, rect->xmax, y);
3307   immEnd();
3308 
3309   GPU_blend(GPU_BLEND_NONE);
3310 
3311   immUnbindProgram();
3312 }
3313 
3314 /** \} */
3315 
3316 /* -------------------------------------------------------------------- */
3317 /** \name Button Draw Callbacks
3318  * \{ */
3319 
widget_numbut_draw(uiWidgetColors * wcol,rcti * rect,int state,int roundboxalign,bool emboss)3320 static void widget_numbut_draw(
3321     uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, bool emboss)
3322 {
3323   uiWidgetBase wtb;
3324   const float rad = wcol->roundness * BLI_rcti_size_y(rect);
3325   const int handle_width = min_ii(BLI_rcti_size_x(rect) / 3, BLI_rcti_size_y(rect) * 0.7f);
3326 
3327   if (state & UI_SELECT) {
3328     SWAP(short, wcol->shadetop, wcol->shadedown);
3329   }
3330 
3331   widget_init(&wtb);
3332 
3333   if (!emboss) {
3334     round_box_edges(&wtb, roundboxalign, rect, rad);
3335   }
3336   else {
3337     wtb.draw_inner = false;
3338     wtb.draw_outline = false;
3339   }
3340 
3341   /* decoration */
3342   if ((state & UI_ACTIVE) && !(state & UI_STATE_TEXT_INPUT)) {
3343     uiWidgetColors wcol_zone;
3344     uiWidgetBase wtb_zone;
3345     rcti rect_zone;
3346     int roundboxalign_zone;
3347 
3348     /* left arrow zone */
3349     widget_init(&wtb_zone);
3350     wtb_zone.draw_outline = false;
3351     wtb_zone.draw_emboss = false;
3352 
3353     wcol_zone = *wcol;
3354     copy_v3_v3_uchar(wcol_zone.item, wcol->text);
3355     if (state & UI_STATE_ACTIVE_LEFT) {
3356       widget_active_color(&wcol_zone);
3357     }
3358 
3359     rect_zone = *rect;
3360     rect_zone.xmax = rect->xmin + handle_width + U.pixelsize;
3361     roundboxalign_zone = roundboxalign & ~(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT);
3362     round_box_edges(&wtb_zone, roundboxalign_zone, &rect_zone, rad);
3363 
3364     shape_preset_init_number_arrows(&wtb_zone.tria1, &rect_zone, 0.6f, 'l');
3365     widgetbase_draw(&wtb_zone, &wcol_zone);
3366 
3367     /* right arrow zone */
3368     widget_init(&wtb_zone);
3369     wtb_zone.draw_outline = false;
3370     wtb_zone.draw_emboss = false;
3371     wtb_zone.tria1.type = ROUNDBOX_TRIA_ARROWS;
3372 
3373     wcol_zone = *wcol;
3374     copy_v3_v3_uchar(wcol_zone.item, wcol->text);
3375     if (state & UI_STATE_ACTIVE_RIGHT) {
3376       widget_active_color(&wcol_zone);
3377     }
3378 
3379     rect_zone = *rect;
3380     rect_zone.xmin = rect->xmax - handle_width - U.pixelsize;
3381     roundboxalign_zone = roundboxalign & ~(UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT);
3382     round_box_edges(&wtb_zone, roundboxalign_zone, &rect_zone, rad);
3383 
3384     shape_preset_init_number_arrows(&wtb_zone.tria2, &rect_zone, 0.6f, 'r');
3385     widgetbase_draw(&wtb_zone, &wcol_zone);
3386 
3387     /* middle highlight zone */
3388     widget_init(&wtb_zone);
3389     wtb_zone.draw_outline = false;
3390     wtb_zone.draw_emboss = false;
3391 
3392     wcol_zone = *wcol;
3393     copy_v3_v3_uchar(wcol_zone.item, wcol->text);
3394     if (!(state & (UI_STATE_ACTIVE_LEFT | UI_STATE_ACTIVE_RIGHT))) {
3395       widget_active_color(&wcol_zone);
3396     }
3397 
3398     rect_zone = *rect;
3399     rect_zone.xmin = rect->xmin + handle_width - U.pixelsize;
3400     rect_zone.xmax = rect->xmax - handle_width + U.pixelsize;
3401     round_box_edges(&wtb_zone, 0, &rect_zone, 0);
3402     widgetbase_draw(&wtb_zone, &wcol_zone);
3403 
3404     /* outline */
3405     wtb.draw_inner = false;
3406     widgetbase_draw(&wtb, wcol);
3407   }
3408   else {
3409     /* inner and outline */
3410     widgetbase_draw(&wtb, wcol);
3411   }
3412 
3413   if (!(state & UI_STATE_TEXT_INPUT)) {
3414     const float textofs = 0.425f * BLI_rcti_size_y(rect);
3415 
3416     /* text space */
3417     rect->xmin += textofs;
3418     rect->xmax -= textofs;
3419   }
3420 }
3421 
widget_numbut(uiWidgetColors * wcol,rcti * rect,int state,int roundboxalign)3422 static void widget_numbut(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
3423 {
3424   widget_numbut_draw(wcol, rect, state, roundboxalign, false);
3425 }
3426 
widget_menubut(uiWidgetColors * wcol,rcti * rect,int UNUSED (state),int roundboxalign)3427 static void widget_menubut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign)
3428 {
3429   uiWidgetBase wtb;
3430   float rad;
3431 
3432   widget_init(&wtb);
3433 
3434   rad = wcol->roundness * U.widget_unit;
3435   round_box_edges(&wtb, roundboxalign, rect, rad);
3436 
3437   /* decoration */
3438   shape_preset_trias_from_rect_menu(&wtb.tria1, rect);
3439   /* copy size and center to 2nd tria */
3440   wtb.tria2 = wtb.tria1;
3441 
3442   widgetbase_draw(&wtb, wcol);
3443 
3444   /* text space, arrows are about 0.6 height of button */
3445   rect->xmax -= (6 * BLI_rcti_size_y(rect)) / 10;
3446 }
3447 
3448 /**
3449  * Draw menu buttons still with triangles when field is not embossed
3450  */
widget_menubut_embossn(uiBut * UNUSED (but),uiWidgetColors * wcol,rcti * rect,int UNUSED (state),int UNUSED (roundboxalign))3451 static void widget_menubut_embossn(uiBut *UNUSED(but),
3452                                    uiWidgetColors *wcol,
3453                                    rcti *rect,
3454                                    int UNUSED(state),
3455                                    int UNUSED(roundboxalign))
3456 {
3457   uiWidgetBase wtb;
3458 
3459   widget_init(&wtb);
3460   wtb.draw_inner = false;
3461   wtb.draw_outline = false;
3462 
3463   /* decoration */
3464   shape_preset_trias_from_rect_menu(&wtb.tria1, rect);
3465   /* copy size and center to 2nd tria */
3466   wtb.tria2 = wtb.tria1;
3467 
3468   widgetbase_draw(&wtb, wcol);
3469 }
3470 
3471 /**
3472  * Draw number buttons still with triangles when field is not embossed
3473  */
widget_numbut_embossn(uiBut * UNUSED (but),uiWidgetColors * wcol,rcti * rect,int state,int roundboxalign)3474 static void widget_numbut_embossn(
3475     uiBut *UNUSED(but), uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
3476 {
3477   widget_numbut_draw(wcol, rect, state, roundboxalign, true);
3478 }
3479 
3480 /* function in use for buttons and for view2d sliders */
UI_draw_widget_scroll(uiWidgetColors * wcol,const rcti * rect,const rcti * slider,int state)3481 void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *slider, int state)
3482 {
3483   uiWidgetBase wtb;
3484   int horizontal;
3485   float rad;
3486   bool outline = false;
3487 
3488   widget_init(&wtb);
3489 
3490   /* determine horizontal/vertical */
3491   horizontal = (BLI_rcti_size_x(rect) > BLI_rcti_size_y(rect));
3492 
3493   if (horizontal) {
3494     rad = wcol->roundness * BLI_rcti_size_y(rect);
3495   }
3496   else {
3497     rad = wcol->roundness * BLI_rcti_size_x(rect);
3498   }
3499 
3500   wtb.uniform_params.shade_dir = (horizontal) ? 1.0f : 0.0;
3501 
3502   /* draw back part, colors swapped and shading inverted */
3503   if (horizontal) {
3504     SWAP(short, wcol->shadetop, wcol->shadedown);
3505   }
3506 
3507   round_box_edges(&wtb, UI_CNR_ALL, rect, rad);
3508   widgetbase_draw(&wtb, wcol);
3509 
3510   /* slider */
3511   if ((BLI_rcti_size_x(slider) < 2) || (BLI_rcti_size_y(slider) < 2)) {
3512     /* pass */
3513   }
3514   else {
3515     SWAP(short, wcol->shadetop, wcol->shadedown);
3516 
3517     copy_v4_v4_uchar(wcol->inner, wcol->item);
3518 
3519     if (wcol->shadetop > wcol->shadedown) {
3520       wcol->shadetop += 20; /* XXX violates themes... */
3521     }
3522     else {
3523       wcol->shadedown += 20;
3524     }
3525 
3526     if (state & UI_SCROLL_PRESSED) {
3527       wcol->inner[0] = wcol->inner[0] >= 250 ? 255 : wcol->inner[0] + 5;
3528       wcol->inner[1] = wcol->inner[1] >= 250 ? 255 : wcol->inner[1] + 5;
3529       wcol->inner[2] = wcol->inner[2] >= 250 ? 255 : wcol->inner[2] + 5;
3530     }
3531 
3532     /* draw */
3533     wtb.draw_emboss = false; /* only emboss once */
3534 
3535     /* exception for progress bar */
3536     if (state & UI_SCROLL_NO_OUTLINE) {
3537       SWAP(bool, outline, wtb.draw_outline);
3538     }
3539 
3540     round_box_edges(&wtb, UI_CNR_ALL, slider, rad);
3541 
3542     if (state & UI_SCROLL_ARROWS) {
3543       if (wcol->item[0] > 48) {
3544         wcol->item[0] -= 48;
3545       }
3546       if (wcol->item[1] > 48) {
3547         wcol->item[1] -= 48;
3548       }
3549       if (wcol->item[2] > 48) {
3550         wcol->item[2] -= 48;
3551       }
3552       wcol->item[3] = 255;
3553 
3554       if (horizontal) {
3555         rcti slider_inset = *slider;
3556         slider_inset.xmin += 0.05 * U.widget_unit;
3557         slider_inset.xmax -= 0.05 * U.widget_unit;
3558         shape_preset_init_scroll_circle(&wtb.tria1, &slider_inset, 0.6f, 'l');
3559         shape_preset_init_scroll_circle(&wtb.tria2, &slider_inset, 0.6f, 'r');
3560       }
3561       else {
3562         shape_preset_init_scroll_circle(&wtb.tria1, slider, 0.6f, 'b');
3563         shape_preset_init_scroll_circle(&wtb.tria2, slider, 0.6f, 't');
3564       }
3565     }
3566     widgetbase_draw(&wtb, wcol);
3567 
3568     if (state & UI_SCROLL_NO_OUTLINE) {
3569       SWAP(bool, outline, wtb.draw_outline);
3570     }
3571   }
3572 }
3573 
widget_scroll(uiBut * but,uiWidgetColors * wcol,rcti * rect,int state,int UNUSED (roundboxalign))3574 static void widget_scroll(
3575     uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int UNUSED(roundboxalign))
3576 {
3577   rcti rect1;
3578   double value;
3579   float fac, size, min;
3580   int horizontal;
3581 
3582   /* calculate slider part */
3583   value = ui_but_value_get(but);
3584 
3585   size = (but->softmax + but->a1 - but->softmin);
3586   size = max_ff(size, 2.0f);
3587 
3588   /* position */
3589   rect1 = *rect;
3590 
3591   /* determine horizontal/vertical */
3592   horizontal = (BLI_rcti_size_x(rect) > BLI_rcti_size_y(rect));
3593 
3594   if (horizontal) {
3595     fac = BLI_rcti_size_x(rect) / size;
3596     rect1.xmin = rect1.xmin + ceilf(fac * ((float)value - but->softmin));
3597     rect1.xmax = rect1.xmin + ceilf(fac * (but->a1 - but->softmin));
3598 
3599     /* ensure minimium size */
3600     min = BLI_rcti_size_y(rect);
3601 
3602     if (BLI_rcti_size_x(&rect1) < min) {
3603       rect1.xmax = rect1.xmin + min;
3604 
3605       if (rect1.xmax > rect->xmax) {
3606         rect1.xmax = rect->xmax;
3607         rect1.xmin = max_ii(rect1.xmax - min, rect->xmin);
3608       }
3609     }
3610   }
3611   else {
3612     fac = BLI_rcti_size_y(rect) / size;
3613     rect1.ymax = rect1.ymax - ceilf(fac * ((float)value - but->softmin));
3614     rect1.ymin = rect1.ymax - ceilf(fac * (but->a1 - but->softmin));
3615 
3616     /* ensure minimium size */
3617     min = BLI_rcti_size_x(rect);
3618 
3619     if (BLI_rcti_size_y(&rect1) < min) {
3620       rect1.ymax = rect1.ymin + min;
3621 
3622       if (rect1.ymax > rect->ymax) {
3623         rect1.ymax = rect->ymax;
3624         rect1.ymin = max_ii(rect1.ymax - min, rect->ymin);
3625       }
3626     }
3627   }
3628 
3629   if (state & UI_SELECT) {
3630     state = UI_SCROLL_PRESSED;
3631   }
3632   else {
3633     state = 0;
3634   }
3635   UI_draw_widget_scroll(wcol, rect, &rect1, state);
3636 }
3637 
widget_progressbar(uiBut * but,uiWidgetColors * wcol,rcti * rect,int UNUSED (state),int roundboxalign)3638 static void widget_progressbar(
3639     uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign)
3640 {
3641   uiButProgressbar *but_progressbar = (uiButProgressbar *)but;
3642   uiWidgetBase wtb, wtb_bar;
3643   rcti rect_prog = *rect, rect_bar = *rect;
3644 
3645   widget_init(&wtb);
3646   widget_init(&wtb_bar);
3647 
3648   /* round corners */
3649   const float value = but_progressbar->progress;
3650   const float offs = wcol->roundness * BLI_rcti_size_y(&rect_prog);
3651   float w = value * BLI_rcti_size_x(&rect_prog);
3652 
3653   /* ensure minimium size */
3654   w = MAX2(w, offs);
3655 
3656   rect_bar.xmax = rect_bar.xmin + w;
3657 
3658   round_box_edges(&wtb, roundboxalign, &rect_prog, offs);
3659   round_box_edges(&wtb_bar, roundboxalign, &rect_bar, offs);
3660 
3661   wtb.draw_outline = true;
3662   widgetbase_draw(&wtb, wcol);
3663 
3664   /* "slider" bar color */
3665   copy_v3_v3_uchar(wcol->inner, wcol->item);
3666   widgetbase_draw(&wtb_bar, wcol);
3667 
3668   /* raise text a bit */
3669   rect->xmin += (BLI_rcti_size_x(&rect_prog) / 2);
3670   rect->xmax += (BLI_rcti_size_x(&rect_prog) / 2);
3671 }
3672 
widget_nodesocket(uiBut * but,uiWidgetColors * wcol,rcti * rect,int UNUSED (state),int UNUSED (roundboxalign))3673 static void widget_nodesocket(
3674     uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign))
3675 {
3676   uiWidgetBase wtb;
3677   const int radi = 5;
3678   uchar old_inner[3], old_outline[3];
3679 
3680   widget_init(&wtb);
3681 
3682   copy_v3_v3_uchar(old_inner, wcol->inner);
3683   copy_v3_v3_uchar(old_outline, wcol->outline);
3684 
3685   wcol->inner[0] = but->col[0];
3686   wcol->inner[1] = but->col[1];
3687   wcol->inner[2] = but->col[2];
3688   wcol->outline[0] = 0;
3689   wcol->outline[1] = 0;
3690   wcol->outline[2] = 0;
3691   wcol->outline[3] = 150;
3692 
3693   const int cent_x = BLI_rcti_cent_x(rect);
3694   const int cent_y = BLI_rcti_cent_y(rect);
3695   rect->xmin = cent_x - radi;
3696   rect->xmax = cent_x + radi;
3697   rect->ymin = cent_y - radi;
3698   rect->ymax = cent_y + radi;
3699 
3700   wtb.draw_outline = true;
3701   round_box_edges(&wtb, UI_CNR_ALL, rect, (float)radi);
3702   widgetbase_draw(&wtb, wcol);
3703 
3704   copy_v3_v3_uchar(wcol->inner, old_inner);
3705   copy_v3_v3_uchar(wcol->outline, old_outline);
3706 }
3707 
widget_numslider(uiBut * but,uiWidgetColors * wcol,rcti * rect,int state,int roundboxalign)3708 static void widget_numslider(
3709     uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
3710 {
3711   uiWidgetBase wtb, wtb1;
3712   rcti rect1;
3713   float offs, toffs;
3714   uchar outline[3];
3715 
3716   widget_init(&wtb);
3717   widget_init(&wtb1);
3718 
3719   /* Backdrop first. */
3720   offs = wcol->roundness * BLI_rcti_size_y(rect);
3721   toffs = offs * 0.75f;
3722   round_box_edges(&wtb, roundboxalign, rect, offs);
3723 
3724   wtb.draw_outline = false;
3725   widgetbase_draw(&wtb, wcol);
3726 
3727   /* Draw slider part only when not in text editing. */
3728   if (!(state & UI_STATE_TEXT_INPUT)) {
3729     int roundboxalign_slider = roundboxalign;
3730 
3731     copy_v3_v3_uchar(outline, wcol->outline);
3732     copy_v3_v3_uchar(wcol->outline, wcol->item);
3733     copy_v3_v3_uchar(wcol->inner, wcol->item);
3734 
3735     if (!(state & UI_SELECT)) {
3736       SWAP(short, wcol->shadetop, wcol->shadedown);
3737     }
3738 
3739     rect1 = *rect;
3740     float factor, factor_ui;
3741     float factor_discard = 1.0f; /* No discard. */
3742     const float value = (float)ui_but_value_get(but);
3743 
3744     if (but->rnaprop && (RNA_property_subtype(but->rnaprop) == PROP_PERCENTAGE)) {
3745       factor = value / but->softmax;
3746     }
3747     else {
3748       factor = (value - but->softmin) / (but->softmax - but->softmin);
3749     }
3750 
3751     const float width = (float)BLI_rcti_size_x(rect);
3752     factor_ui = factor * width;
3753 
3754     if (factor_ui <= offs) {
3755       /* Left part only. */
3756       roundboxalign_slider &= ~(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT);
3757       rect1.xmax = rect1.xmin + offs;
3758       factor_discard = factor_ui / offs;
3759     }
3760     else if (factor_ui <= width - offs) {
3761       /* Left part + middle part. */
3762       roundboxalign_slider &= ~(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT);
3763       rect1.xmax = rect1.xmin + factor_ui;
3764     }
3765     else {
3766       /* Left part + middle part + right part. */
3767       factor_discard = factor;
3768     }
3769 
3770     round_box_edges(&wtb1, roundboxalign_slider, &rect1, offs);
3771     wtb1.draw_outline = false;
3772     widgetbase_set_uniform_discard_factor(&wtb1, factor_discard);
3773     widgetbase_draw(&wtb1, wcol);
3774 
3775     copy_v3_v3_uchar(wcol->outline, outline);
3776 
3777     if (!(state & UI_SELECT)) {
3778       SWAP(short, wcol->shadetop, wcol->shadedown);
3779     }
3780   }
3781 
3782   /* Outline. */
3783   wtb.draw_outline = true;
3784   wtb.draw_inner = false;
3785   widgetbase_draw(&wtb, wcol);
3786 
3787   /* Add space at either side of the button so text aligns with numbuttons
3788    * (which have arrow icons). */
3789   if (!(state & UI_STATE_TEXT_INPUT)) {
3790     rect->xmax -= toffs;
3791     rect->xmin += toffs;
3792   }
3793 }
3794 
3795 /* I think 3 is sufficient border to indicate keyed status */
3796 #define SWATCH_KEYED_BORDER 3
3797 
widget_swatch(uiBut * but,uiWidgetColors * wcol,rcti * rect,int state,int roundboxalign)3798 static void widget_swatch(
3799     uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
3800 {
3801   BLI_assert(but->type == UI_BTYPE_COLOR);
3802   uiButColor *color_but = (uiButColor *)but;
3803   uiWidgetBase wtb;
3804   float rad, col[4];
3805 
3806   col[3] = 1.0f;
3807 
3808   if (but->rnaprop) {
3809     BLI_assert(but->rnaindex == -1);
3810 
3811     if (RNA_property_array_length(&but->rnapoin, but->rnaprop) == 4) {
3812       col[3] = RNA_property_float_get_index(&but->rnapoin, but->rnaprop, 3);
3813     }
3814   }
3815 
3816   widget_init(&wtb);
3817 
3818   rad = wcol->roundness * U.widget_unit;
3819   round_box_edges(&wtb, roundboxalign, rect, rad);
3820 
3821   ui_but_v3_get(but, col);
3822 
3823   if ((state & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_OVERRIDEN |
3824                 UI_BUT_REDALERT)) ||
3825       (but->drawflag & UI_BUT_ANIMATED_CHANGED)) {
3826     /* draw based on state - color for keyed etc */
3827     widgetbase_draw(&wtb, wcol);
3828 
3829     /* inset to draw swatch color */
3830     rect->xmin += SWATCH_KEYED_BORDER;
3831     rect->xmax -= SWATCH_KEYED_BORDER;
3832     rect->ymin += SWATCH_KEYED_BORDER;
3833     rect->ymax -= SWATCH_KEYED_BORDER;
3834 
3835     round_box_edges(&wtb, roundboxalign, rect, rad);
3836   }
3837 
3838   if (!ui_but_is_color_gamma(but)) {
3839     ui_block_cm_to_display_space_v3(but->block, col);
3840   }
3841 
3842   rgba_float_to_uchar(wcol->inner, col);
3843   const bool show_alpha_checkers = (wcol->inner[3] < 255);
3844 
3845   wcol->shaded = 0;
3846 
3847   /* Now we reduce alpha of the inner color (i.e. the color shown)
3848    * so that this setting can look grayed out, while retaining
3849    * the checkerboard (for transparent values). This is needed
3850    * here as the effects of ui_widget_color_disabled() are overwritten. */
3851   wcol->inner[3] *= widget_alpha_factor(state);
3852 
3853   widgetbase_draw_ex(&wtb, wcol, show_alpha_checkers);
3854   if (color_but->is_pallete_color &&
3855       ((Palette *)but->rnapoin.owner_id)->active_color == color_but->palette_color_index) {
3856     const float width = rect->xmax - rect->xmin;
3857     const float height = rect->ymax - rect->ymin;
3858     /* find color luminance and change it slightly */
3859     float bw = rgb_to_grayscale(col);
3860 
3861     bw += (bw < 0.5f) ? 0.5f : -0.5f;
3862 
3863     /* We are drawing on top of widget bases. Flush cache. */
3864     GPU_blend(GPU_BLEND_ALPHA);
3865     UI_widgetbase_draw_cache_flush();
3866     GPU_blend(GPU_BLEND_NONE);
3867 
3868     const uint pos = GPU_vertformat_attr_add(
3869         immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
3870     immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
3871 
3872     immUniformColor3f(bw, bw, bw);
3873     immBegin(GPU_PRIM_TRIS, 3);
3874     immVertex2f(pos, rect->xmin + 0.1f * width, rect->ymin + 0.9f * height);
3875     immVertex2f(pos, rect->xmin + 0.1f * width, rect->ymin + 0.5f * height);
3876     immVertex2f(pos, rect->xmin + 0.5f * width, rect->ymin + 0.9f * height);
3877     immEnd();
3878 
3879     immUnbindProgram();
3880   }
3881 }
3882 
widget_unitvec(uiBut * but,uiWidgetColors * wcol,rcti * rect,int UNUSED (state),int UNUSED (roundboxalign))3883 static void widget_unitvec(
3884     uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign))
3885 {
3886   ui_draw_but_UNITVEC(but, wcol, rect);
3887 }
3888 
widget_icon_has_anim(uiBut * but,uiWidgetColors * wcol,rcti * rect,int state,int roundboxalign)3889 static void widget_icon_has_anim(
3890     uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
3891 {
3892   if (state & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_REDALERT)) {
3893     uiWidgetBase wtb;
3894     float rad;
3895 
3896     widget_init(&wtb);
3897     wtb.draw_outline = false;
3898 
3899     rad = wcol->roundness * BLI_rcti_size_y(rect);
3900     round_box_edges(&wtb, UI_CNR_ALL, rect, rad);
3901     widgetbase_draw(&wtb, wcol);
3902   }
3903   else if (but->type == UI_BTYPE_NUM) {
3904     /* Draw number buttons still with left/right
3905      * triangles when field is not embossed */
3906     widget_numbut_embossn(but, wcol, rect, state, roundboxalign);
3907   }
3908   else if (but->type == UI_BTYPE_MENU) {
3909     /* Draw menu buttons still with down arrow. */
3910     widget_menubut_embossn(but, wcol, rect, state, roundboxalign);
3911   }
3912 }
3913 
widget_textbut(uiWidgetColors * wcol,rcti * rect,int state,int roundboxalign)3914 static void widget_textbut(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
3915 {
3916   uiWidgetBase wtb;
3917   float rad;
3918 
3919   if (state & UI_SELECT) {
3920     SWAP(short, wcol->shadetop, wcol->shadedown);
3921   }
3922 
3923   widget_init(&wtb);
3924 
3925   rad = wcol->roundness * U.widget_unit;
3926   round_box_edges(&wtb, roundboxalign, rect, rad);
3927 
3928   widgetbase_draw(&wtb, wcol);
3929 }
3930 
widget_menuiconbut(uiWidgetColors * wcol,rcti * rect,int UNUSED (state),int roundboxalign)3931 static void widget_menuiconbut(uiWidgetColors *wcol,
3932                                rcti *rect,
3933                                int UNUSED(state),
3934                                int roundboxalign)
3935 {
3936   uiWidgetBase wtb;
3937   float rad;
3938 
3939   widget_init(&wtb);
3940 
3941   rad = wcol->roundness * U.widget_unit;
3942   round_box_edges(&wtb, roundboxalign, rect, rad);
3943 
3944   /* decoration */
3945   widgetbase_draw(&wtb, wcol);
3946 }
3947 
widget_pulldownbut(uiWidgetColors * wcol,rcti * rect,int state,int roundboxalign)3948 static void widget_pulldownbut(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
3949 {
3950   float back[4];
3951   UI_GetThemeColor4fv(TH_BACK, back);
3952 
3953   if ((state & UI_ACTIVE) || (back[3] < 1.0f)) {
3954     uiWidgetBase wtb;
3955     const float rad = wcol->roundness * U.widget_unit;
3956 
3957     if (state & UI_ACTIVE) {
3958       copy_v4_v4_uchar(wcol->inner, wcol->inner_sel);
3959       copy_v3_v3_uchar(wcol->text, wcol->text_sel);
3960       copy_v3_v3_uchar(wcol->outline, wcol->inner);
3961     }
3962     else {
3963       wcol->inner[3] *= 1.0f - back[3];
3964       wcol->outline[3] = 0.0f;
3965     }
3966 
3967     widget_init(&wtb);
3968 
3969     /* half rounded */
3970     round_box_edges(&wtb, roundboxalign, rect, rad);
3971 
3972     widgetbase_draw(&wtb, wcol);
3973   }
3974 }
3975 
widget_menu_itembut(uiWidgetColors * wcol,rcti * rect,int UNUSED (state),int UNUSED (roundboxalign))3976 static void widget_menu_itembut(uiWidgetColors *wcol,
3977                                 rcti *rect,
3978                                 int UNUSED(state),
3979                                 int UNUSED(roundboxalign))
3980 {
3981   uiWidgetBase wtb;
3982 
3983   widget_init(&wtb);
3984 
3985   /* not rounded, no outline */
3986   wtb.draw_outline = false;
3987   round_box_edges(&wtb, 0, rect, 0.0f);
3988 
3989   widgetbase_draw(&wtb, wcol);
3990 }
3991 
widget_menu_radial_itembut(uiBut * but,uiWidgetColors * wcol,rcti * rect,int UNUSED (state),int UNUSED (roundboxalign))3992 static void widget_menu_radial_itembut(
3993     uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign))
3994 {
3995   uiWidgetBase wtb;
3996   float rad;
3997   const float fac = but->block->pie_data.alphafac;
3998 
3999   widget_init(&wtb);
4000 
4001   wtb.draw_emboss = false;
4002 
4003   rad = wcol->roundness * BLI_rcti_size_y(rect);
4004   round_box_edges(&wtb, UI_CNR_ALL, rect, rad);
4005 
4006   wcol->inner[3] *= fac;
4007   wcol->inner_sel[3] *= fac;
4008   wcol->item[3] *= fac;
4009   wcol->text[3] *= fac;
4010   wcol->text_sel[3] *= fac;
4011   wcol->outline[3] *= fac;
4012 
4013   widgetbase_draw(&wtb, wcol);
4014 }
4015 
widget_list_itembut(uiWidgetColors * wcol,rcti * rect,int UNUSED (state),int UNUSED (roundboxalign))4016 static void widget_list_itembut(uiWidgetColors *wcol,
4017                                 rcti *rect,
4018                                 int UNUSED(state),
4019                                 int UNUSED(roundboxalign))
4020 {
4021   uiWidgetBase wtb;
4022   float rad;
4023 
4024   widget_init(&wtb);
4025 
4026   /* no outline */
4027   wtb.draw_outline = false;
4028   rad = wcol->roundness * U.widget_unit;
4029   round_box_edges(&wtb, UI_CNR_ALL, rect, rad);
4030 
4031   widgetbase_draw(&wtb, wcol);
4032 }
4033 
widget_optionbut(uiWidgetColors * wcol,rcti * rect,int state,int UNUSED (roundboxalign))4034 static void widget_optionbut(uiWidgetColors *wcol,
4035                              rcti *rect,
4036                              int state,
4037                              int UNUSED(roundboxalign))
4038 {
4039   const bool text_before_widget = (state & UI_STATE_TEXT_BEFORE_WIDGET);
4040   uiWidgetBase wtb;
4041   rcti recttemp = *rect;
4042   float rad;
4043   int delta;
4044 
4045   widget_init(&wtb);
4046 
4047   /* square */
4048   if (text_before_widget) {
4049     recttemp.xmin = recttemp.xmax - BLI_rcti_size_y(&recttemp);
4050   }
4051   else {
4052     recttemp.xmax = recttemp.xmin + BLI_rcti_size_y(&recttemp);
4053   }
4054 
4055   /* smaller */
4056   delta = 1 + BLI_rcti_size_y(&recttemp) / 8;
4057   BLI_rcti_resize(
4058       &recttemp, BLI_rcti_size_x(&recttemp) - delta * 2, BLI_rcti_size_y(&recttemp) - delta * 2);
4059   /* Keep one edge in place. */
4060   BLI_rcti_translate(&recttemp, text_before_widget ? delta : -delta, 0);
4061 
4062   rad = wcol->roundness * BLI_rcti_size_y(&recttemp);
4063   round_box_edges(&wtb, UI_CNR_ALL, &recttemp, rad);
4064 
4065   /* decoration */
4066   if (state & UI_SELECT) {
4067     shape_preset_trias_from_rect_checkmark(&wtb.tria1, &recttemp);
4068   }
4069 
4070   widgetbase_draw(&wtb, wcol);
4071 
4072   /* Text space - factor is really just eyeballed. */
4073   const float offset = delta * 0.9;
4074   if (text_before_widget) {
4075     rect->xmax = recttemp.xmin - offset;
4076   }
4077   else {
4078     rect->xmin = recttemp.xmax + offset;
4079   }
4080 }
4081 
4082 /* labels use Editor theme colors for text */
widget_state_label(uiWidgetType * wt,int state,int drawflag)4083 static void widget_state_label(uiWidgetType *wt, int state, int drawflag)
4084 {
4085   if (state & UI_BUT_LIST_ITEM) {
4086     /* Override default label theme's colors. */
4087     bTheme *btheme = UI_GetTheme();
4088     wt->wcol_theme = &btheme->tui.wcol_list_item;
4089     /* call this for option button */
4090     widget_state(wt, state, drawflag);
4091   }
4092   else {
4093     /* call this for option button */
4094     widget_state(wt, state, drawflag);
4095     if (state & UI_SELECT) {
4096       UI_GetThemeColor3ubv(TH_TEXT_HI, wt->wcol.text);
4097     }
4098     else {
4099       UI_GetThemeColor3ubv(TH_TEXT, wt->wcol.text);
4100     }
4101   }
4102 
4103   if (state & UI_BUT_REDALERT) {
4104     const uchar red[4] = {255, 0, 0};
4105     color_blend_v3_v3(wt->wcol.text, red, 0.4f);
4106   }
4107 }
4108 
widget_radiobut(uiWidgetColors * wcol,rcti * rect,int UNUSED (state),int roundboxalign)4109 static void widget_radiobut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign)
4110 {
4111   uiWidgetBase wtb;
4112   float rad;
4113 
4114   widget_init(&wtb);
4115 
4116   rad = wcol->roundness * U.widget_unit;
4117   round_box_edges(&wtb, roundboxalign, rect, rad);
4118 
4119   widgetbase_draw(&wtb, wcol);
4120 }
4121 
widget_box(uiBut * but,uiWidgetColors * wcol,rcti * rect,int UNUSED (state),int roundboxalign)4122 static void widget_box(
4123     uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign)
4124 {
4125   uiWidgetBase wtb;
4126   float rad;
4127   uchar old_col[3];
4128 
4129   widget_init(&wtb);
4130 
4131   copy_v3_v3_uchar(old_col, wcol->inner);
4132 
4133   /* abuse but->hsv - if it's non-zero, use this color as the box's background */
4134   if (but != NULL && but->col[3]) {
4135     wcol->inner[0] = but->col[0];
4136     wcol->inner[1] = but->col[1];
4137     wcol->inner[2] = but->col[2];
4138     wcol->inner[3] = but->col[3];
4139   }
4140 
4141   rad = wcol->roundness * U.widget_unit;
4142   round_box_edges(&wtb, roundboxalign, rect, rad);
4143 
4144   widgetbase_draw(&wtb, wcol);
4145 
4146   copy_v3_v3_uchar(wcol->inner, old_col);
4147 }
4148 
widget_but(uiWidgetColors * wcol,rcti * rect,int UNUSED (state),int roundboxalign)4149 static void widget_but(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign)
4150 {
4151   uiWidgetBase wtb;
4152   float rad;
4153 
4154   widget_init(&wtb);
4155 
4156   rad = wcol->roundness * U.widget_unit;
4157   round_box_edges(&wtb, roundboxalign, rect, rad);
4158 
4159   widgetbase_draw(&wtb, wcol);
4160 }
4161 
4162 #if 0
4163 static void widget_roundbut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign)
4164 {
4165   uiWidgetBase wtb;
4166   const float rad = wcol->roundness * U.widget_unit;
4167 
4168   widget_init(&wtb);
4169 
4170   /* half rounded */
4171   round_box_edges(&wtb, roundboxalign, rect, rad);
4172 
4173   widgetbase_draw(&wtb, wcol);
4174 }
4175 #endif
4176 
widget_roundbut_exec(uiWidgetColors * wcol,rcti * rect,int state,int roundboxalign)4177 static void widget_roundbut_exec(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
4178 {
4179   uiWidgetBase wtb;
4180   const float rad = wcol->roundness * U.widget_unit;
4181 
4182   widget_init(&wtb);
4183 
4184   if (state & UI_STATE_HOLD_ACTION) {
4185     /* Show that keeping pressed performs another action (typically a menu). */
4186     shape_preset_init_hold_action(&wtb.tria1, rect, 0.75f, 'r');
4187   }
4188 
4189   /* half rounded */
4190   round_box_edges(&wtb, roundboxalign, rect, rad);
4191 
4192   widgetbase_draw(&wtb, wcol);
4193 }
4194 
widget_tab(uiWidgetColors * wcol,rcti * rect,int state,int roundboxalign)4195 static void widget_tab(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
4196 {
4197   const float rad = wcol->roundness * U.widget_unit;
4198   const bool is_active = (state & UI_SELECT);
4199 
4200   /* Draw shaded outline - Disabled for now,
4201    * seems incorrect and also looks nicer without it imho ;) */
4202   // #define USE_TAB_SHADED_HIGHLIGHT
4203 
4204   uiWidgetBase wtb;
4205   uchar theme_col_tab_highlight[3];
4206 
4207 #ifdef USE_TAB_SHADED_HIGHLIGHT
4208   /* create outline highlight colors */
4209   if (is_active) {
4210     interp_v3_v3v3_uchar(theme_col_tab_highlight, wcol->inner_sel, wcol->outline, 0.2f);
4211   }
4212   else {
4213     interp_v3_v3v3_uchar(theme_col_tab_highlight, wcol->inner, wcol->outline, 0.12f);
4214   }
4215 #endif
4216 
4217   widget_init(&wtb);
4218 
4219   /* half rounded */
4220   round_box_edges(&wtb, roundboxalign, rect, rad);
4221 
4222   /* draw inner */
4223 #ifdef USE_TAB_SHADED_HIGHLIGHT
4224   wtb.draw_outline = 0;
4225 #endif
4226   widgetbase_draw(&wtb, wcol);
4227 
4228   /* We are drawing on top of widget bases. Flush cache. */
4229   GPU_blend(GPU_BLEND_ALPHA);
4230   UI_widgetbase_draw_cache_flush();
4231   GPU_blend(GPU_BLEND_NONE);
4232 
4233 #ifdef USE_TAB_SHADED_HIGHLIGHT
4234   /* draw outline (3d look) */
4235   ui_draw_but_TAB_outline(rect, rad, theme_col_tab_highlight, wcol->inner);
4236 #endif
4237 
4238 #ifndef USE_TAB_SHADED_HIGHLIGHT
4239   UNUSED_VARS(is_active, theme_col_tab_highlight);
4240 #endif
4241 }
4242 
widget_draw_extra_mask(const bContext * C,uiBut * but,uiWidgetType * wt,rcti * rect)4243 static void widget_draw_extra_mask(const bContext *C, uiBut *but, uiWidgetType *wt, rcti *rect)
4244 {
4245   bTheme *btheme = UI_GetTheme();
4246   uiWidgetColors *wcol = &btheme->tui.wcol_radio;
4247   uiWidgetBase wtb;
4248   const float rad = wcol->roundness * U.widget_unit;
4249   uchar col[4];
4250 
4251   /* state copy! */
4252   wt->wcol = *(wt->wcol_theme);
4253 
4254   widget_init(&wtb);
4255 
4256   if (but->block->drawextra) {
4257     /* note: drawextra can change rect +1 or -1, to match round errors of existing previews */
4258     but->block->drawextra(
4259         C, but->poin, but->block->drawextra_arg1, but->block->drawextra_arg2, rect);
4260 
4261     const uint pos = GPU_vertformat_attr_add(
4262         immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
4263     immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
4264 
4265     /* make mask to draw over image */
4266     UI_GetThemeColor3ubv(TH_BACK, col);
4267     immUniformColor3ubv(col);
4268 
4269     round_box__edges(&wtb, UI_CNR_ALL, rect, 0.0f, rad);
4270     widgetbase_outline(&wtb, pos);
4271 
4272     immUnbindProgram();
4273   }
4274 
4275   /* outline */
4276   round_box_edges(&wtb, UI_CNR_ALL, rect, rad);
4277   wtb.draw_outline = true;
4278   wtb.draw_inner = false;
4279   widgetbase_draw(&wtb, &wt->wcol);
4280 }
4281 
widget_type(uiWidgetTypeEnum type)4282 static uiWidgetType *widget_type(uiWidgetTypeEnum type)
4283 {
4284   bTheme *btheme = UI_GetTheme();
4285   static uiWidgetType wt;
4286 
4287   /* defaults */
4288   wt.wcol_theme = &btheme->tui.wcol_regular;
4289   wt.wcol_state = &btheme->tui.wcol_state;
4290   wt.state = widget_state;
4291   wt.draw = widget_but;
4292   wt.custom = NULL;
4293   wt.text = widget_draw_text_icon;
4294 
4295   switch (type) {
4296     case UI_WTYPE_REGULAR:
4297       break;
4298 
4299     case UI_WTYPE_LABEL:
4300       wt.draw = NULL;
4301       wt.state = widget_state_label;
4302       break;
4303 
4304     case UI_WTYPE_TOGGLE:
4305       wt.wcol_theme = &btheme->tui.wcol_toggle;
4306       break;
4307 
4308     case UI_WTYPE_CHECKBOX:
4309       wt.wcol_theme = &btheme->tui.wcol_option;
4310       wt.draw = widget_optionbut;
4311       break;
4312 
4313     case UI_WTYPE_RADIO:
4314       wt.wcol_theme = &btheme->tui.wcol_radio;
4315       wt.draw = widget_radiobut;
4316       break;
4317 
4318     case UI_WTYPE_NUMBER:
4319       wt.wcol_theme = &btheme->tui.wcol_num;
4320       wt.draw = widget_numbut;
4321       break;
4322 
4323     case UI_WTYPE_SLIDER:
4324       wt.wcol_theme = &btheme->tui.wcol_numslider;
4325       wt.custom = widget_numslider;
4326       wt.state = widget_state_numslider;
4327       break;
4328 
4329     case UI_WTYPE_EXEC:
4330       wt.wcol_theme = &btheme->tui.wcol_tool;
4331       wt.draw = widget_roundbut_exec;
4332       break;
4333 
4334     case UI_WTYPE_TOOLBAR_ITEM:
4335       wt.wcol_theme = &btheme->tui.wcol_toolbar_item;
4336       wt.draw = widget_roundbut_exec;
4337       break;
4338 
4339     case UI_WTYPE_TAB:
4340       wt.wcol_theme = &btheme->tui.wcol_tab;
4341       wt.draw = widget_tab;
4342       break;
4343 
4344     case UI_WTYPE_TOOLTIP:
4345       wt.wcol_theme = &btheme->tui.wcol_tooltip;
4346       wt.draw = widget_menu_back;
4347       break;
4348 
4349     /* strings */
4350     case UI_WTYPE_NAME:
4351       wt.wcol_theme = &btheme->tui.wcol_text;
4352       wt.draw = widget_textbut;
4353       break;
4354 
4355     case UI_WTYPE_NAME_LINK:
4356       break;
4357 
4358     case UI_WTYPE_POINTER_LINK:
4359       break;
4360 
4361     case UI_WTYPE_FILENAME:
4362       break;
4363 
4364     /* start menus */
4365     case UI_WTYPE_MENU_RADIO:
4366       wt.wcol_theme = &btheme->tui.wcol_menu;
4367       wt.draw = widget_menubut;
4368       break;
4369 
4370     case UI_WTYPE_MENU_ICON_RADIO:
4371     case UI_WTYPE_MENU_NODE_LINK:
4372       wt.wcol_theme = &btheme->tui.wcol_menu;
4373       wt.draw = widget_menuiconbut;
4374       break;
4375 
4376     case UI_WTYPE_MENU_POINTER_LINK:
4377       wt.wcol_theme = &btheme->tui.wcol_menu;
4378       wt.draw = widget_menubut;
4379       break;
4380 
4381     case UI_WTYPE_PULLDOWN:
4382       wt.wcol_theme = &btheme->tui.wcol_pulldown;
4383       wt.draw = widget_pulldownbut;
4384       wt.state = widget_state_pulldown;
4385       break;
4386 
4387     /* in menus */
4388     case UI_WTYPE_MENU_ITEM:
4389       wt.wcol_theme = &btheme->tui.wcol_menu_item;
4390       wt.draw = widget_menu_itembut;
4391       wt.state = widget_state_menu_item;
4392       break;
4393 
4394     case UI_WTYPE_MENU_BACK:
4395       wt.wcol_theme = &btheme->tui.wcol_menu_back;
4396       wt.draw = widget_menu_back;
4397       break;
4398 
4399     /* specials */
4400     case UI_WTYPE_ICON:
4401       wt.custom = widget_icon_has_anim;
4402       break;
4403 
4404     case UI_WTYPE_ICON_LABEL:
4405       /* behave like regular labels (this is simply a label with an icon) */
4406       wt.state = widget_state_label;
4407       wt.custom = widget_icon_has_anim;
4408       break;
4409 
4410     case UI_WTYPE_SWATCH:
4411       wt.custom = widget_swatch;
4412       break;
4413 
4414     case UI_WTYPE_BOX:
4415       wt.custom = widget_box;
4416       wt.wcol_theme = &btheme->tui.wcol_box;
4417       break;
4418 
4419     case UI_WTYPE_RGB_PICKER:
4420       break;
4421 
4422     case UI_WTYPE_UNITVEC:
4423       wt.custom = widget_unitvec;
4424       break;
4425 
4426     case UI_WTYPE_SCROLL:
4427       wt.wcol_theme = &btheme->tui.wcol_scroll;
4428       wt.state = widget_state_nothing;
4429       wt.custom = widget_scroll;
4430       break;
4431 
4432     case UI_WTYPE_LISTITEM:
4433       wt.wcol_theme = &btheme->tui.wcol_list_item;
4434       wt.draw = widget_list_itembut;
4435       break;
4436 
4437     case UI_WTYPE_PROGRESSBAR:
4438       wt.wcol_theme = &btheme->tui.wcol_progress;
4439       wt.custom = widget_progressbar;
4440       break;
4441 
4442     case UI_WTYPE_NODESOCKET:
4443       wt.custom = widget_nodesocket;
4444       break;
4445 
4446     case UI_WTYPE_MENU_ITEM_RADIAL:
4447       wt.wcol_theme = &btheme->tui.wcol_pie_menu;
4448       wt.custom = widget_menu_radial_itembut;
4449       wt.state = widget_state_pie_menu_item;
4450       break;
4451   }
4452 
4453   return &wt;
4454 }
4455 
widget_roundbox_set(uiBut * but,rcti * rect)4456 static int widget_roundbox_set(uiBut *but, rcti *rect)
4457 {
4458   int roundbox = UI_CNR_ALL;
4459 
4460   /* alignment */
4461   if ((but->drawflag & UI_BUT_ALIGN) && but->type != UI_BTYPE_PULLDOWN) {
4462 
4463     /* ui_popup_block_position has this correction too, keep in sync */
4464     if (but->drawflag & (UI_BUT_ALIGN_TOP | UI_BUT_ALIGN_STITCH_TOP)) {
4465       rect->ymax += U.pixelsize;
4466     }
4467     if (but->drawflag & (UI_BUT_ALIGN_LEFT | UI_BUT_ALIGN_STITCH_LEFT)) {
4468       rect->xmin -= U.pixelsize;
4469     }
4470 
4471     switch (but->drawflag & UI_BUT_ALIGN) {
4472       case UI_BUT_ALIGN_TOP:
4473         roundbox = UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT;
4474         break;
4475       case UI_BUT_ALIGN_DOWN:
4476         roundbox = UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT;
4477         break;
4478       case UI_BUT_ALIGN_LEFT:
4479         roundbox = UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT;
4480         break;
4481       case UI_BUT_ALIGN_RIGHT:
4482         roundbox = UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT;
4483         break;
4484       case UI_BUT_ALIGN_DOWN | UI_BUT_ALIGN_RIGHT:
4485         roundbox = UI_CNR_TOP_LEFT;
4486         break;
4487       case UI_BUT_ALIGN_DOWN | UI_BUT_ALIGN_LEFT:
4488         roundbox = UI_CNR_TOP_RIGHT;
4489         break;
4490       case UI_BUT_ALIGN_TOP | UI_BUT_ALIGN_RIGHT:
4491         roundbox = UI_CNR_BOTTOM_LEFT;
4492         break;
4493       case UI_BUT_ALIGN_TOP | UI_BUT_ALIGN_LEFT:
4494         roundbox = UI_CNR_BOTTOM_RIGHT;
4495         break;
4496       default:
4497         roundbox = 0;
4498         break;
4499     }
4500   }
4501 
4502   /* align with open menu */
4503   if (but->active && (but->type != UI_BTYPE_POPOVER) && !ui_but_menu_draw_as_popover(but)) {
4504     const int direction = ui_but_menu_direction(but);
4505 
4506     if (direction == UI_DIR_UP) {
4507       roundbox &= ~(UI_CNR_TOP_RIGHT | UI_CNR_TOP_LEFT);
4508     }
4509     else if (direction == UI_DIR_DOWN) {
4510       roundbox &= ~(UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT);
4511     }
4512     else if (direction == UI_DIR_LEFT) {
4513       roundbox &= ~(UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT);
4514     }
4515     else if (direction == UI_DIR_RIGHT) {
4516       roundbox &= ~(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT);
4517     }
4518   }
4519 
4520   return roundbox;
4521 }
4522 
4523 /* -------------------------------------------------------------------- */
4524 /** \name Public API
4525  * \{ */
4526 
4527 /* conversion from old to new buttons, so still messy */
ui_draw_but(const bContext * C,struct ARegion * region,uiStyle * style,uiBut * but,rcti * rect)4528 void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBut *but, rcti *rect)
4529 {
4530   bTheme *btheme = UI_GetTheme();
4531   const ThemeUI *tui = &btheme->tui;
4532   const uiFontStyle *fstyle = &style->widget;
4533   uiWidgetType *wt = NULL;
4534 
4535   /* handle menus separately */
4536   if (but->emboss == UI_EMBOSS_PULLDOWN) {
4537     switch (but->type) {
4538       case UI_BTYPE_LABEL:
4539         widget_draw_text_icon(&style->widgetlabel, &tui->wcol_menu_back, but, rect);
4540         break;
4541       case UI_BTYPE_SEPR_LINE:
4542         ui_draw_separator(rect, &tui->wcol_menu_item);
4543         break;
4544       default:
4545         wt = widget_type(UI_WTYPE_MENU_ITEM);
4546         break;
4547     }
4548   }
4549   else if (but->emboss == UI_EMBOSS_NONE) {
4550     /* "nothing" */
4551     switch (but->type) {
4552       case UI_BTYPE_LABEL:
4553         wt = widget_type(UI_WTYPE_ICON_LABEL);
4554         break;
4555       default:
4556         wt = widget_type(UI_WTYPE_ICON);
4557         break;
4558     }
4559   }
4560   else if (but->emboss == UI_EMBOSS_RADIAL) {
4561     wt = widget_type(UI_WTYPE_MENU_ITEM_RADIAL);
4562   }
4563   else {
4564     BLI_assert(but->emboss == UI_EMBOSS);
4565 
4566     switch (but->type) {
4567       case UI_BTYPE_LABEL:
4568         wt = widget_type(UI_WTYPE_LABEL);
4569         fstyle = &style->widgetlabel;
4570         if (but->drawflag & UI_BUT_BOX_ITEM) {
4571           wt->wcol_theme = &tui->wcol_box;
4572           wt->state = widget_state;
4573         }
4574         else if (but->block->theme_style == UI_BLOCK_THEME_STYLE_POPUP) {
4575           wt->wcol_theme = &tui->wcol_menu_back;
4576           wt->state = widget_state;
4577         }
4578         break;
4579 
4580       case UI_BTYPE_SEPR:
4581       case UI_BTYPE_SEPR_LINE:
4582       case UI_BTYPE_SEPR_SPACER:
4583         break;
4584 
4585       case UI_BTYPE_BUT:
4586       case UI_BTYPE_DECORATOR:
4587 #ifdef USE_UI_TOOLBAR_HACK
4588         if ((but->icon != ICON_NONE) && UI_but_is_tool(but)) {
4589           wt = widget_type(UI_WTYPE_TOOLBAR_ITEM);
4590         }
4591         else {
4592           wt = widget_type(UI_WTYPE_EXEC);
4593         }
4594 #else
4595         wt = widget_type(UI_WTYPE_EXEC);
4596 #endif
4597         break;
4598 
4599       case UI_BTYPE_NUM:
4600         wt = widget_type(UI_WTYPE_NUMBER);
4601         break;
4602 
4603       case UI_BTYPE_NUM_SLIDER:
4604         wt = widget_type(UI_WTYPE_SLIDER);
4605         break;
4606 
4607       case UI_BTYPE_ROW:
4608         wt = widget_type(UI_WTYPE_RADIO);
4609         break;
4610 
4611       case UI_BTYPE_LISTROW:
4612         wt = widget_type(UI_WTYPE_LISTITEM);
4613         break;
4614 
4615       case UI_BTYPE_TEXT:
4616         wt = widget_type(UI_WTYPE_NAME);
4617         break;
4618 
4619       case UI_BTYPE_SEARCH_MENU:
4620         wt = widget_type(UI_WTYPE_NAME);
4621         break;
4622 
4623       case UI_BTYPE_TAB:
4624         wt = widget_type(UI_WTYPE_TAB);
4625         break;
4626 
4627       case UI_BTYPE_BUT_TOGGLE:
4628       case UI_BTYPE_TOGGLE:
4629       case UI_BTYPE_TOGGLE_N:
4630         wt = widget_type(UI_WTYPE_TOGGLE);
4631         break;
4632 
4633       case UI_BTYPE_CHECKBOX:
4634       case UI_BTYPE_CHECKBOX_N:
4635         if (!(but->flag & UI_HAS_ICON)) {
4636           wt = widget_type(UI_WTYPE_CHECKBOX);
4637 
4638           if ((but->drawflag & (UI_BUT_TEXT_LEFT | UI_BUT_TEXT_RIGHT)) == 0) {
4639             but->drawflag |= UI_BUT_TEXT_LEFT;
4640           }
4641           /* #widget_optionbut() carefully sets the text rectangle for fine tuned paddings. If the
4642            * text drawing were to add its own padding, DPI and zoom factor would be applied twice
4643            * in the final padding, so it's difficult to control it. */
4644           but->drawflag |= UI_BUT_NO_TEXT_PADDING;
4645         }
4646         else {
4647           wt = widget_type(UI_WTYPE_TOGGLE);
4648         }
4649 
4650         /* option buttons have strings outside, on menus use different colors */
4651         if (but->block->theme_style == UI_BLOCK_THEME_STYLE_POPUP) {
4652           wt->state = widget_state_option_menu;
4653         }
4654         break;
4655 
4656       case UI_BTYPE_MENU:
4657       case UI_BTYPE_BLOCK:
4658       case UI_BTYPE_POPOVER:
4659         if (but->flag & UI_BUT_NODE_LINK) {
4660           /* new node-link button, not active yet XXX */
4661           wt = widget_type(UI_WTYPE_MENU_NODE_LINK);
4662         }
4663         else {
4664           /* with menu arrows */
4665 
4666           /* We could use a flag for this, but for now just check size,
4667            * add up/down arrows if there is room. */
4668           if ((!but->str[0] && but->icon && (BLI_rcti_size_x(rect) < BLI_rcti_size_y(rect) + 2)) ||
4669               /* disable for brushes also */
4670               (but->flag & UI_BUT_ICON_PREVIEW)) {
4671             /* no arrows */
4672             wt = widget_type(UI_WTYPE_MENU_ICON_RADIO);
4673           }
4674           else {
4675             wt = widget_type(UI_WTYPE_MENU_RADIO);
4676           }
4677         }
4678         break;
4679 
4680       case UI_BTYPE_PULLDOWN:
4681         wt = widget_type(UI_WTYPE_PULLDOWN);
4682         break;
4683 
4684       case UI_BTYPE_BUT_MENU:
4685         wt = widget_type(UI_WTYPE_MENU_ITEM);
4686         break;
4687 
4688       case UI_BTYPE_COLOR:
4689         wt = widget_type(UI_WTYPE_SWATCH);
4690         break;
4691 
4692       case UI_BTYPE_ROUNDBOX:
4693       case UI_BTYPE_LISTBOX:
4694         wt = widget_type(UI_WTYPE_BOX);
4695         break;
4696 
4697       case UI_BTYPE_EXTRA:
4698         widget_draw_extra_mask(C, but, widget_type(UI_WTYPE_BOX), rect);
4699         break;
4700 
4701       case UI_BTYPE_HSVCUBE: {
4702         const uiButHSVCube *hsv_but = (uiButHSVCube *)but;
4703 
4704         if (ELEM(hsv_but->gradient_type, UI_GRAD_V_ALT, UI_GRAD_L_ALT)) {
4705           /* vertical V slider, uses new widget draw now */
4706           ui_draw_but_HSV_v(but, rect);
4707         }
4708         else { /* other HSV pickers... */
4709           ui_draw_but_HSVCUBE(but, rect);
4710         }
4711         break;
4712       }
4713 
4714       case UI_BTYPE_HSVCIRCLE:
4715         ui_draw_but_HSVCIRCLE(but, &tui->wcol_regular, rect);
4716         break;
4717 
4718       case UI_BTYPE_COLORBAND:
4719         /* do not draw right to edge of rect */
4720         rect->xmin += (0.25f * UI_UNIT_X);
4721         rect->xmax -= (0.3f * UI_UNIT_X);
4722         ui_draw_but_COLORBAND(but, &tui->wcol_regular, rect);
4723         break;
4724 
4725       case UI_BTYPE_UNITVEC:
4726         wt = widget_type(UI_WTYPE_UNITVEC);
4727         break;
4728 
4729       case UI_BTYPE_IMAGE:
4730         ui_draw_but_IMAGE(region, but, &tui->wcol_regular, rect);
4731         break;
4732 
4733       case UI_BTYPE_HISTOGRAM:
4734         ui_draw_but_HISTOGRAM(region, but, &tui->wcol_regular, rect);
4735         break;
4736 
4737       case UI_BTYPE_WAVEFORM:
4738         ui_draw_but_WAVEFORM(region, but, &tui->wcol_regular, rect);
4739         break;
4740 
4741       case UI_BTYPE_VECTORSCOPE:
4742         ui_draw_but_VECTORSCOPE(region, but, &tui->wcol_regular, rect);
4743         break;
4744 
4745       case UI_BTYPE_CURVE:
4746         /* do not draw right to edge of rect */
4747         rect->xmin += (0.2f * UI_UNIT_X);
4748         rect->xmax -= (0.2f * UI_UNIT_X);
4749         ui_draw_but_CURVE(region, but, &tui->wcol_regular, rect);
4750         break;
4751 
4752       case UI_BTYPE_CURVEPROFILE:
4753         ui_draw_but_CURVEPROFILE(region, but, &tui->wcol_regular, rect);
4754         break;
4755 
4756       case UI_BTYPE_PROGRESS_BAR:
4757         wt = widget_type(UI_WTYPE_PROGRESSBAR);
4758         fstyle = &style->widgetlabel;
4759         break;
4760 
4761       case UI_BTYPE_SCROLL:
4762         wt = widget_type(UI_WTYPE_SCROLL);
4763         break;
4764 
4765       case UI_BTYPE_GRIP:
4766         wt = widget_type(UI_WTYPE_ICON);
4767         break;
4768 
4769       case UI_BTYPE_TRACK_PREVIEW:
4770         ui_draw_but_TRACKPREVIEW(region, but, &tui->wcol_regular, rect);
4771         break;
4772 
4773       case UI_BTYPE_NODE_SOCKET:
4774         wt = widget_type(UI_WTYPE_NODESOCKET);
4775         break;
4776 
4777       default:
4778         wt = widget_type(UI_WTYPE_REGULAR);
4779         break;
4780     }
4781   }
4782 
4783   if (wt) {
4784     // rcti disablerect = *rect; /* rect gets clipped smaller for text */
4785     int roundboxalign, state, drawflag;
4786 
4787     roundboxalign = widget_roundbox_set(but, rect);
4788 
4789     /* Mask out flags re-used for local state. */
4790     state = but->flag & ~UI_STATE_FLAGS_ALL;
4791     drawflag = but->drawflag;
4792 
4793     if (state & UI_SELECT_DRAW) {
4794       state |= UI_SELECT;
4795     }
4796 
4797     if ((but->editstr) ||
4798         (UNLIKELY(but->flag & UI_BUT_DRAG_MULTI) && ui_but_drag_multi_edit_get(but))) {
4799       state |= UI_STATE_TEXT_INPUT;
4800     }
4801 
4802     if (but->hold_func) {
4803       state |= UI_STATE_HOLD_ACTION;
4804     }
4805 
4806     if (state & UI_ACTIVE) {
4807       if (but->drawflag & UI_BUT_ACTIVE_LEFT) {
4808         state |= UI_STATE_ACTIVE_LEFT;
4809       }
4810       else if (but->drawflag & UI_BUT_ACTIVE_RIGHT) {
4811         state |= UI_STATE_ACTIVE_RIGHT;
4812       }
4813     }
4814 
4815     bool use_alpha_blend = false;
4816     if (but->emboss != UI_EMBOSS_PULLDOWN) {
4817       if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_SEARCH_FILTER_NO_MATCH)) {
4818         use_alpha_blend = true;
4819         ui_widget_color_disabled(wt, state);
4820       }
4821     }
4822 
4823     if (drawflag & UI_BUT_TEXT_RIGHT) {
4824       state |= UI_STATE_TEXT_BEFORE_WIDGET;
4825     }
4826 
4827 #ifdef USE_UI_POPOVER_ONCE
4828     if (but->block->flag & UI_BLOCK_POPOVER_ONCE) {
4829       if ((state & UI_ACTIVE) && ui_but_is_popover_once_compat(but)) {
4830         state |= UI_BUT_ACTIVE_DEFAULT;
4831       }
4832     }
4833 #endif
4834 
4835     wt->state(wt, state, drawflag);
4836     if (wt->custom) {
4837       wt->custom(but, &wt->wcol, rect, state, roundboxalign);
4838     }
4839     else if (wt->draw) {
4840       wt->draw(&wt->wcol, rect, state, roundboxalign);
4841     }
4842 
4843     if (use_alpha_blend) {
4844       GPU_blend(GPU_BLEND_ALPHA);
4845     }
4846 
4847     wt->text(fstyle, &wt->wcol, but, rect);
4848     if (use_alpha_blend) {
4849       GPU_blend(GPU_BLEND_NONE);
4850     }
4851   }
4852 }
4853 
ui_draw_clip_tri(uiBlock * block,rcti * rect,uiWidgetType * wt)4854 static void ui_draw_clip_tri(uiBlock *block, rcti *rect, uiWidgetType *wt)
4855 {
4856   if (block) {
4857     float draw_color[4];
4858     const uchar *color = wt->wcol.text;
4859 
4860     draw_color[0] = ((float)color[0]) / 255.0f;
4861     draw_color[1] = ((float)color[1]) / 255.0f;
4862     draw_color[2] = ((float)color[2]) / 255.0f;
4863     draw_color[3] = 1.0f;
4864 
4865     if (block->flag & UI_BLOCK_CLIPTOP) {
4866       /* XXX no scaling for UI here yet */
4867       UI_draw_icon_tri(BLI_rcti_cent_x(rect), rect->ymax - 8, 't', draw_color);
4868     }
4869     if (block->flag & UI_BLOCK_CLIPBOTTOM) {
4870       /* XXX no scaling for UI here yet */
4871       UI_draw_icon_tri(BLI_rcti_cent_x(rect), rect->ymin + 10, 'v', draw_color);
4872     }
4873   }
4874 }
4875 
ui_draw_menu_back(uiStyle * UNUSED (style),uiBlock * block,rcti * rect)4876 void ui_draw_menu_back(uiStyle *UNUSED(style), uiBlock *block, rcti *rect)
4877 {
4878   uiWidgetType *wt = widget_type(UI_WTYPE_MENU_BACK);
4879 
4880   wt->state(wt, 0, 0);
4881   if (block) {
4882     wt->draw(&wt->wcol, rect, block->flag, block->direction);
4883   }
4884   else {
4885     wt->draw(&wt->wcol, rect, 0, 0);
4886   }
4887 
4888   ui_draw_clip_tri(block, rect, wt);
4889 }
4890 
4891 /**
4892  * Uses the widget base drawing and colors from from the box widget, but ensures an opaque
4893  * inner color.
4894  */
ui_draw_box_opaque(rcti * rect,int roundboxalign)4895 void ui_draw_box_opaque(rcti *rect, int roundboxalign)
4896 {
4897   uiWidgetType *wt = widget_type(UI_WTYPE_BOX);
4898 
4899   /* Alpha blend with the region's background color to force an opaque background. */
4900   uiWidgetColors *wcol = &wt->wcol;
4901   wt->state(wt, 0, 0);
4902   float background[4];
4903   UI_GetThemeColor4fv(TH_BACK, background);
4904   float new_inner[4];
4905   rgba_uchar_to_float(new_inner, wcol->inner);
4906   new_inner[0] = (new_inner[0] * new_inner[3]) + (background[0] * (1.0f - new_inner[3]));
4907   new_inner[1] = (new_inner[1] * new_inner[3]) + (background[1] * (1.0f - new_inner[3]));
4908   new_inner[2] = (new_inner[2] * new_inner[3]) + (background[2] * (1.0f - new_inner[3]));
4909   new_inner[3] = 1.0f;
4910   rgba_float_to_uchar(wcol->inner, new_inner);
4911 
4912   wt->custom(NULL, wcol, rect, 0, roundboxalign);
4913 }
4914 
4915 /**
4916  * Similar to 'widget_menu_back', however we can't use the widget preset system
4917  * because we need to pass in the original location so we know where to show the arrow.
4918  */
ui_draw_popover_back_impl(const uiWidgetColors * wcol,rcti * rect,int direction,const float unit_size,const float mval_origin[2])4919 static void ui_draw_popover_back_impl(const uiWidgetColors *wcol,
4920                                       rcti *rect,
4921                                       int direction,
4922                                       const float unit_size,
4923                                       const float mval_origin[2])
4924 {
4925   /* tsk, this isn't nice. */
4926   const float unit_half = unit_size / 2;
4927   const float cent_x = mval_origin ? CLAMPIS(mval_origin[0],
4928                                              rect->xmin + unit_size,
4929                                              rect->xmax - unit_size) :
4930                                      BLI_rcti_cent_x(rect);
4931 
4932   GPU_blend(GPU_BLEND_ALPHA);
4933 
4934   /* Extracted from 'widget_menu_back', keep separate to avoid menu changes breaking popovers */
4935   {
4936     uiWidgetBase wtb;
4937     widget_init(&wtb);
4938 
4939     const int roundboxalign = UI_CNR_ALL;
4940     widget_softshadow(rect, roundboxalign, wcol->roundness * U.widget_unit);
4941 
4942     round_box_edges(&wtb, roundboxalign, rect, wcol->roundness * U.widget_unit);
4943     wtb.draw_emboss = false;
4944     widgetbase_draw(&wtb, wcol);
4945   }
4946 
4947   /* Draw popover arrow (top/bottom) */
4948   if (ELEM(direction, UI_DIR_UP, UI_DIR_DOWN)) {
4949     const uint pos = GPU_vertformat_attr_add(
4950         immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
4951     immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
4952 
4953     const bool is_down = (direction == UI_DIR_DOWN);
4954     const int sign = is_down ? 1 : -1;
4955     float y = is_down ? rect->ymax : rect->ymin;
4956 
4957     GPU_blend(GPU_BLEND_ALPHA);
4958     immBegin(GPU_PRIM_TRIS, 3);
4959     immUniformColor4ubv(wcol->outline);
4960     immVertex2f(pos, cent_x - unit_half, y);
4961     immVertex2f(pos, cent_x + unit_half, y);
4962     immVertex2f(pos, cent_x, y + sign * unit_half);
4963     immEnd();
4964 
4965     y = y - sign * round(U.pixelsize * 1.41);
4966 
4967     GPU_blend(GPU_BLEND_NONE);
4968     immBegin(GPU_PRIM_TRIS, 3);
4969     immUniformColor4ub(0, 0, 0, 0);
4970     immVertex2f(pos, cent_x - unit_half, y);
4971     immVertex2f(pos, cent_x + unit_half, y);
4972     immVertex2f(pos, cent_x, y + sign * unit_half);
4973     immEnd();
4974 
4975     GPU_blend(GPU_BLEND_ALPHA);
4976     immBegin(GPU_PRIM_TRIS, 3);
4977     immUniformColor4ubv(wcol->inner);
4978     immVertex2f(pos, cent_x - unit_half, y);
4979     immVertex2f(pos, cent_x + unit_half, y);
4980     immVertex2f(pos, cent_x, y + sign * unit_half);
4981     immEnd();
4982 
4983     immUnbindProgram();
4984   }
4985 
4986   GPU_blend(GPU_BLEND_NONE);
4987 }
4988 
ui_draw_popover_back(struct ARegion * region,uiStyle * UNUSED (style),uiBlock * block,rcti * rect)4989 void ui_draw_popover_back(struct ARegion *region,
4990                           uiStyle *UNUSED(style),
4991                           uiBlock *block,
4992                           rcti *rect)
4993 {
4994   uiWidgetType *wt = widget_type(UI_WTYPE_MENU_BACK);
4995 
4996   if (block) {
4997     float mval_origin[2] = {UNPACK2(block->bounds_offset)};
4998     ui_window_to_block_fl(region, block, &mval_origin[0], &mval_origin[1]);
4999     ui_draw_popover_back_impl(
5000         wt->wcol_theme, rect, block->direction, U.widget_unit / block->aspect, mval_origin);
5001   }
5002   else {
5003     wt->state(wt, 0, 0);
5004     wt->draw(&wt->wcol, rect, 0, 0);
5005   }
5006 
5007   ui_draw_clip_tri(block, rect, wt);
5008 }
5009 
draw_disk_shaded(float start,float angle,float radius_int,float radius_ext,int subd,const uchar col1[4],const uchar col2[4],bool shaded)5010 static void draw_disk_shaded(float start,
5011                              float angle,
5012                              float radius_int,
5013                              float radius_ext,
5014                              int subd,
5015                              const uchar col1[4],
5016                              const uchar col2[4],
5017                              bool shaded)
5018 {
5019   const float radius_ext_scale = (0.5f / radius_ext); /* 1 / (2 * radius_ext) */
5020 
5021   uint pos, col;
5022 
5023   GPUVertFormat *format = immVertexFormat();
5024   pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
5025   if (shaded) {
5026     col = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
5027     immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
5028   }
5029   else {
5030     immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
5031     immUniformColor4ubv(col1);
5032   }
5033 
5034   immBegin(GPU_PRIM_TRI_STRIP, subd * 2);
5035   for (int i = 0; i < subd; i++) {
5036     float a = start + ((i) / (float)(subd - 1)) * angle;
5037     float s = sinf(a);
5038     float c = cosf(a);
5039     float y1 = s * radius_int;
5040     float y2 = s * radius_ext;
5041 
5042     if (shaded) {
5043       uchar r_col[4];
5044       float fac = (y1 + radius_ext) * radius_ext_scale;
5045       color_blend_v4_v4v4(r_col, col1, col2, fac);
5046       immAttr4ubv(col, r_col);
5047     }
5048     immVertex2f(pos, c * radius_int, s * radius_int);
5049 
5050     if (shaded) {
5051       uchar r_col[4];
5052       float fac = (y2 + radius_ext) * radius_ext_scale;
5053       color_blend_v4_v4v4(r_col, col1, col2, fac);
5054       immAttr4ubv(col, r_col);
5055     }
5056     immVertex2f(pos, c * radius_ext, s * radius_ext);
5057   }
5058   immEnd();
5059 
5060   immUnbindProgram();
5061 }
5062 
ui_draw_pie_center(uiBlock * block)5063 void ui_draw_pie_center(uiBlock *block)
5064 {
5065   bTheme *btheme = UI_GetTheme();
5066   const float cx = block->pie_data.pie_center_spawned[0];
5067   const float cy = block->pie_data.pie_center_spawned[1];
5068 
5069   float *pie_dir = block->pie_data.pie_dir;
5070 
5071   const float pie_radius_internal = U.dpi_fac * U.pie_menu_threshold;
5072   const float pie_radius_external = U.dpi_fac * (U.pie_menu_threshold + 7.0f);
5073 
5074   const int subd = 40;
5075 
5076   const float angle = atan2f(pie_dir[1], pie_dir[0]);
5077   const float range = (block->pie_data.flags & UI_PIE_DEGREES_RANGE_LARGE) ? M_PI_2 : M_PI_4;
5078 
5079   GPU_matrix_push();
5080   GPU_matrix_translate_2f(cx, cy);
5081 
5082   GPU_blend(GPU_BLEND_ALPHA);
5083   if (btheme->tui.wcol_pie_menu.shaded) {
5084     uchar col1[4], col2[4];
5085     shadecolors4(col1,
5086                  col2,
5087                  btheme->tui.wcol_pie_menu.inner,
5088                  btheme->tui.wcol_pie_menu.shadetop,
5089                  btheme->tui.wcol_pie_menu.shadedown);
5090     draw_disk_shaded(0.0f,
5091                      (float)(M_PI * 2.0),
5092                      pie_radius_internal,
5093                      pie_radius_external,
5094                      subd,
5095                      col1,
5096                      col2,
5097                      true);
5098   }
5099   else {
5100     draw_disk_shaded(0.0f,
5101                      (float)(M_PI * 2.0),
5102                      pie_radius_internal,
5103                      pie_radius_external,
5104                      subd,
5105                      btheme->tui.wcol_pie_menu.inner,
5106                      NULL,
5107                      false);
5108   }
5109 
5110   if (!(block->pie_data.flags & UI_PIE_INVALID_DIR)) {
5111     if (btheme->tui.wcol_pie_menu.shaded) {
5112       uchar col1[4], col2[4];
5113       shadecolors4(col1,
5114                    col2,
5115                    btheme->tui.wcol_pie_menu.inner_sel,
5116                    btheme->tui.wcol_pie_menu.shadetop,
5117                    btheme->tui.wcol_pie_menu.shadedown);
5118       draw_disk_shaded(angle - range / 2.0f,
5119                        range,
5120                        pie_radius_internal,
5121                        pie_radius_external,
5122                        subd,
5123                        col1,
5124                        col2,
5125                        true);
5126     }
5127     else {
5128       draw_disk_shaded(angle - range / 2.0f,
5129                        range,
5130                        pie_radius_internal,
5131                        pie_radius_external,
5132                        subd,
5133                        btheme->tui.wcol_pie_menu.inner_sel,
5134                        NULL,
5135                        false);
5136     }
5137   }
5138 
5139   GPUVertFormat *format = immVertexFormat();
5140   const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
5141   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
5142   immUniformColor4ubv(btheme->tui.wcol_pie_menu.outline);
5143 
5144   imm_draw_circle_wire_2d(pos, 0.0f, 0.0f, pie_radius_internal, subd);
5145   imm_draw_circle_wire_2d(pos, 0.0f, 0.0f, pie_radius_external, subd);
5146 
5147   immUnbindProgram();
5148 
5149   if (U.pie_menu_confirm > 0 &&
5150       !(block->pie_data.flags & (UI_PIE_INVALID_DIR | UI_PIE_CLICK_STYLE))) {
5151     const float pie_confirm_radius = U.dpi_fac * (pie_radius_internal + U.pie_menu_confirm);
5152     const float pie_confirm_external = U.dpi_fac *
5153                                        (pie_radius_internal + U.pie_menu_confirm + 7.0f);
5154 
5155     const uchar col[4] = {UNPACK3(btheme->tui.wcol_pie_menu.text_sel), 64};
5156     draw_disk_shaded(angle - range / 2.0f,
5157                      range,
5158                      pie_confirm_radius,
5159                      pie_confirm_external,
5160                      subd,
5161                      col,
5162                      NULL,
5163                      false);
5164   }
5165 
5166   GPU_blend(GPU_BLEND_NONE);
5167   GPU_matrix_pop();
5168 }
5169 
ui_tooltip_get_theme(void)5170 const uiWidgetColors *ui_tooltip_get_theme(void)
5171 {
5172   uiWidgetType *wt = widget_type(UI_WTYPE_TOOLTIP);
5173   return wt->wcol_theme;
5174 }
5175 
5176 /**
5177  * Generic drawing for background.
5178  */
ui_draw_widget_back_color(uiWidgetTypeEnum type,bool use_shadow,const rcti * rect,const float color[4])5179 static void ui_draw_widget_back_color(uiWidgetTypeEnum type,
5180                                       bool use_shadow,
5181                                       const rcti *rect,
5182                                       const float color[4])
5183 {
5184   uiWidgetType *wt = widget_type(type);
5185 
5186   if (use_shadow) {
5187     GPU_blend(GPU_BLEND_ALPHA);
5188     widget_softshadow(rect, UI_CNR_ALL, 0.25f * U.widget_unit);
5189     GPU_blend(GPU_BLEND_NONE);
5190   }
5191 
5192   rcti rect_copy = *rect;
5193   wt->state(wt, 0, 0);
5194   if (color) {
5195     rgba_float_to_uchar(wt->wcol.inner, color);
5196   }
5197   wt->draw(&wt->wcol, &rect_copy, 0, UI_CNR_ALL);
5198 }
ui_draw_widget_menu_back_color(const rcti * rect,bool use_shadow,const float color[4])5199 void ui_draw_widget_menu_back_color(const rcti *rect, bool use_shadow, const float color[4])
5200 {
5201   ui_draw_widget_back_color(UI_WTYPE_MENU_BACK, use_shadow, rect, color);
5202 }
5203 
ui_draw_widget_menu_back(const rcti * rect,bool use_shadow)5204 void ui_draw_widget_menu_back(const rcti *rect, bool use_shadow)
5205 {
5206   ui_draw_widget_back_color(UI_WTYPE_MENU_BACK, use_shadow, rect, NULL);
5207 }
5208 
ui_draw_tooltip_background(const uiStyle * UNUSED (style),uiBlock * UNUSED (block),rcti * rect)5209 void ui_draw_tooltip_background(const uiStyle *UNUSED(style), uiBlock *UNUSED(block), rcti *rect)
5210 {
5211   uiWidgetType *wt = widget_type(UI_WTYPE_TOOLTIP);
5212   wt->state(wt, 0, 0);
5213   /* wt->draw ends up using same function to draw the tooltip as menu_back */
5214   wt->draw(&wt->wcol, rect, 0, 0);
5215 }
5216 
5217 /**
5218  * Helper call to draw a menu item without a button.
5219  *
5220  * \param state: The state of the button,
5221  * typically #UI_ACTIVE, #UI_BUT_DISABLED, #UI_BUT_INACTIVE.
5222  * \param use_sep: When true, characters after the last #UI_SEP_CHAR are right aligned,
5223  * use for displaying key shortcuts.
5224  * \param r_xmax: The right hand position of the text, this takes into the icon,
5225  * padding and text clipping when there is not enough room to display the full text.
5226  */
ui_draw_menu_item(const uiFontStyle * fstyle,rcti * rect,const char * name,int iconid,int state,bool use_sep,int * r_xmax)5227 void ui_draw_menu_item(const uiFontStyle *fstyle,
5228                        rcti *rect,
5229                        const char *name,
5230                        int iconid,
5231                        int state,
5232                        bool use_sep,
5233                        int *r_xmax)
5234 {
5235   uiWidgetType *wt = widget_type(UI_WTYPE_MENU_ITEM);
5236   const rcti _rect = *rect;
5237   char *cpoin = NULL;
5238 
5239   wt->state(wt, state, 0);
5240   wt->draw(&wt->wcol, rect, 0, 0);
5241 
5242   UI_fontstyle_set(fstyle);
5243 
5244   /* text location offset */
5245   rect->xmin += 0.25f * UI_UNIT_X;
5246   if (iconid) {
5247     rect->xmin += UI_DPI_ICON_SIZE;
5248   }
5249 
5250   /* cut string in 2 parts? */
5251   if (use_sep) {
5252     cpoin = strrchr(name, UI_SEP_CHAR);
5253     if (cpoin) {
5254       *cpoin = 0;
5255 
5256       /* need to set this first */
5257       UI_fontstyle_set(fstyle);
5258 
5259       if (fstyle->kerning == 1) {
5260         /* for BLF_width */
5261         BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
5262       }
5263 
5264       rect->xmax -= BLF_width(fstyle->uifont_id, cpoin + 1, INT_MAX) + UI_DPI_ICON_SIZE;
5265 
5266       if (fstyle->kerning == 1) {
5267         BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
5268       }
5269     }
5270   }
5271 
5272   {
5273     char drawstr[UI_MAX_DRAW_STR];
5274     const float okwidth = (float)BLI_rcti_size_x(rect);
5275     const size_t max_len = sizeof(drawstr);
5276     const float minwidth = (float)(UI_DPI_ICON_SIZE);
5277 
5278     BLI_strncpy(drawstr, name, sizeof(drawstr));
5279     if (drawstr[0]) {
5280       UI_text_clip_middle_ex(fstyle, drawstr, okwidth, minwidth, max_len, '\0');
5281     }
5282 
5283     int xofs = 0, yofs = 0;
5284     struct ResultBLF info;
5285     UI_fontstyle_draw_ex(fstyle,
5286                          rect,
5287                          drawstr,
5288                          wt->wcol.text,
5289                          &(struct uiFontStyleDraw_Params){
5290                              .align = UI_STYLE_TEXT_LEFT,
5291                          },
5292                          BLF_DRAW_STR_DUMMY_MAX,
5293                          &xofs,
5294                          &yofs,
5295                          &info);
5296     if (r_xmax != NULL) {
5297       *r_xmax = xofs + info.width;
5298     }
5299   }
5300 
5301   /* restore rect, was messed with */
5302   *rect = _rect;
5303 
5304   if (iconid) {
5305     float height, aspect;
5306     const int xs = rect->xmin + 0.2f * UI_UNIT_X;
5307     const int ys = rect->ymin + 0.1f * BLI_rcti_size_y(rect);
5308 
5309     height = ICON_SIZE_FROM_BUTRECT(rect);
5310     aspect = ICON_DEFAULT_HEIGHT / height;
5311 
5312     GPU_blend(GPU_BLEND_ALPHA);
5313     /* XXX scale weak get from fstyle? */
5314     UI_icon_draw_ex(xs, ys, iconid, aspect, 1.0f, 0.0f, wt->wcol.text, false);
5315     GPU_blend(GPU_BLEND_NONE);
5316   }
5317 
5318   /* part text right aligned */
5319   if (use_sep) {
5320     if (cpoin) {
5321       /* Set inactive state for grayed out text. */
5322       wt->state(wt, state | UI_BUT_INACTIVE, 0);
5323 
5324       rect->xmax = _rect.xmax - 5;
5325       UI_fontstyle_draw(fstyle,
5326                         rect,
5327                         cpoin + 1,
5328                         wt->wcol.text,
5329                         &(struct uiFontStyleDraw_Params){
5330                             .align = UI_STYLE_TEXT_RIGHT,
5331                         });
5332       *cpoin = UI_SEP_CHAR;
5333     }
5334   }
5335 }
5336 
ui_draw_preview_item(const uiFontStyle * fstyle,rcti * rect,const char * name,int iconid,int state)5337 void ui_draw_preview_item(
5338     const uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, int state)
5339 {
5340   rcti trect = *rect;
5341   const float text_size = UI_UNIT_Y;
5342   float font_dims[2] = {0.0f, 0.0f};
5343   uiWidgetType *wt = widget_type(UI_WTYPE_MENU_ITEM);
5344 
5345   /* drawing button background */
5346   wt->state(wt, state, 0);
5347   wt->draw(&wt->wcol, rect, 0, 0);
5348 
5349   /* draw icon in rect above the space reserved for the label */
5350   rect->ymin += text_size;
5351   GPU_blend(GPU_BLEND_ALPHA);
5352   widget_draw_preview(iconid, 1.0f, rect);
5353   GPU_blend(GPU_BLEND_NONE);
5354 
5355   BLF_width_and_height(
5356       fstyle->uifont_id, name, BLF_DRAW_STR_DUMMY_MAX, &font_dims[0], &font_dims[1]);
5357 
5358   /* text rect */
5359   trect.xmin += 0;
5360   trect.xmax = trect.xmin + font_dims[0] + U.widget_unit / 2;
5361   trect.ymin += U.widget_unit / 2;
5362   trect.ymax = trect.ymin + font_dims[1];
5363   if (trect.xmax > rect->xmax - PREVIEW_PAD) {
5364     trect.xmax = rect->xmax - PREVIEW_PAD;
5365   }
5366 
5367   {
5368     char drawstr[UI_MAX_DRAW_STR];
5369     const float okwidth = (float)BLI_rcti_size_x(&trect);
5370     const size_t max_len = sizeof(drawstr);
5371     const float minwidth = (float)(UI_DPI_ICON_SIZE);
5372 
5373     BLI_strncpy(drawstr, name, sizeof(drawstr));
5374     UI_text_clip_middle_ex(fstyle, drawstr, okwidth, minwidth, max_len, '\0');
5375 
5376     UI_fontstyle_draw(fstyle,
5377                       &trect,
5378                       drawstr,
5379                       wt->wcol.text,
5380                       &(struct uiFontStyleDraw_Params){
5381                           .align = UI_STYLE_TEXT_CENTER,
5382                       });
5383   }
5384 }
5385 
5386 /** \} */
5387