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