UI: Color Picker, make HSV default.
[blender.git] / source / blender / editors / interface / interface_region_color_picker.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2008 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file \ingroup edinterface
21  *
22  * Color Picker Region & Color Utils
23  */
24
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <assert.h>
29
30 #include "MEM_guardedalloc.h"
31
32 #include "DNA_userdef_types.h"
33
34 #include "BLI_utildefines.h"
35 #include "BLI_math.h"
36 #include "BLI_listbase.h"
37 #include "BLI_string.h"
38
39 #include "BKE_context.h"
40
41 #include "WM_types.h"
42
43 #include "RNA_access.h"
44
45 #include "UI_interface.h"
46
47 #include "BLT_translation.h"
48
49 #include "ED_screen.h"
50
51 #include "IMB_colormanagement.h"
52
53 #include "interface_intern.h"
54
55 enum ePickerType {
56         PICKER_TYPE_RGB = 0,
57         PICKER_TYPE_HSV = 1,
58         PICKER_TYPE_HEX = 2,
59 };
60
61 /* -------------------------------------------------------------------- */
62 /** \name Color Conversion
63  * \{ */
64
65 void ui_rgb_to_color_picker_compat_v(const float rgb[3], float r_cp[3])
66 {
67         switch (U.color_picker_type) {
68                 case USER_CP_CIRCLE_HSL:
69                         rgb_to_hsl_compat_v(rgb, r_cp);
70                         break;
71                 default:
72                         rgb_to_hsv_compat_v(rgb, r_cp);
73                         break;
74         }
75 }
76
77 void ui_rgb_to_color_picker_v(const float rgb[3], float r_cp[3])
78 {
79         switch (U.color_picker_type) {
80                 case USER_CP_CIRCLE_HSL:
81                         rgb_to_hsl_v(rgb, r_cp);
82                         break;
83                 default:
84                         rgb_to_hsv_v(rgb, r_cp);
85                         break;
86         }
87 }
88
89 void ui_color_picker_to_rgb_v(const float r_cp[3], float rgb[3])
90 {
91         switch (U.color_picker_type) {
92                 case USER_CP_CIRCLE_HSL:
93                         hsl_to_rgb_v(r_cp, rgb);
94                         break;
95                 default:
96                         hsv_to_rgb_v(r_cp, rgb);
97                         break;
98         }
99 }
100
101 void ui_color_picker_to_rgb(float r_cp0, float r_cp1, float r_cp2, float *r, float *g, float *b)
102 {
103         switch (U.color_picker_type) {
104                 case USER_CP_CIRCLE_HSL:
105                         hsl_to_rgb(r_cp0, r_cp1, r_cp2, r, g, b);
106                         break;
107                 default:
108                         hsv_to_rgb(r_cp0, r_cp1, r_cp2, r, g, b);
109                         break;
110         }
111 }
112
113 /* Returns true if the button is for a color with gamma baked in,
114  * or if it's a color picker for such a button. */
115 bool ui_but_is_color_gamma(uiBut *but)
116 {
117         if (but->rnaprop) {
118                 if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) {
119                         return true;
120                 }
121         }
122
123         return but->block->is_color_gamma_picker;
124 }
125
126 void ui_scene_linear_to_color_picker_space(uiBut *but, float rgb[3])
127 {
128         /* Map to color picking space for HSV values and HSV cube/circle,
129          * assuming it is more perceptually linear then the scene linear
130          * space for intuitive color picking. */
131         if (!ui_but_is_color_gamma(but)) {
132                 IMB_colormanagement_scene_linear_to_color_picking_v3(rgb);
133         }
134 }
135
136 void ui_color_picker_to_scene_linear_space(uiBut *but, float rgb[3])
137 {
138         if (!ui_but_is_color_gamma(but)) {
139                 IMB_colormanagement_color_picking_to_scene_linear_v3(rgb);
140         }
141 }
142
143 /** \} */
144
145 /* -------------------------------------------------------------------- */
146 /** \name Color Picker
147  * \{ */
148
149 /* for picker, while editing hsv */
150 void ui_but_hsv_set(uiBut *but)
151 {
152         float col[3];
153         ColorPicker *cpicker = but->custom_data;
154         float *hsv = cpicker->color_data;
155
156         ui_color_picker_to_rgb_v(hsv, col);
157
158         ui_but_v3_set(but, col);
159 }
160
161 /* Updates all buttons who share the same color picker as the one passed
162  * also used by small picker, be careful with name checks below... */
163 static void ui_update_color_picker_buts_rgb(
164         uiBut *from_but, uiBlock *block, ColorPicker *cpicker, const float rgb[3])
165 {
166         uiBut *bt;
167         float *hsv = cpicker->color_data;
168
169         /* Convert from RGB to HSV in perceptually linear space. */
170         float tmp[3];
171         copy_v3_v3(tmp, rgb);
172         if (from_but) {
173                 ui_scene_linear_to_color_picker_space(from_but, tmp);
174         }
175         ui_rgb_to_color_picker_compat_v(tmp, hsv);
176
177         /* this updates button strings,
178          * is hackish... but button pointers are on stack of caller function */
179         for (bt = block->buttons.first; bt; bt = bt->next) {
180                 if (bt->custom_data != cpicker)
181                         continue;
182
183                 if (bt->rnaprop) {
184                         ui_but_v3_set(bt, rgb);
185
186                         /* original button that created the color picker already does undo
187                          * push, so disable it on RNA buttons in the color picker block */
188                         UI_but_flag_disable(bt, UI_BUT_UNDO);
189                 }
190                 else if (STREQ(bt->str, "Hex: ")) {
191                         float rgb_hex[3];
192                         uchar rgb_hex_uchar[3];
193                         double intpart;
194                         char col[16];
195
196                         /* Hex code is assumed to be in sRGB space
197                          * (coming from other applications, web, etc) */
198                         copy_v3_v3(rgb_hex, rgb);
199                         if (from_but && !ui_but_is_color_gamma(from_but)) {
200                                 IMB_colormanagement_scene_linear_to_srgb_v3(rgb_hex);
201                         }
202
203                         if (rgb_hex[0] > 1.0f) rgb_hex[0] = modf(rgb_hex[0], &intpart);
204                         if (rgb_hex[1] > 1.0f) rgb_hex[1] = modf(rgb_hex[1], &intpart);
205                         if (rgb_hex[2] > 1.0f) rgb_hex[2] = modf(rgb_hex[2], &intpart);
206
207                         rgb_float_to_uchar(rgb_hex_uchar, rgb_hex);
208                         BLI_snprintf(col, sizeof(col), "%02X%02X%02X", UNPACK3_EX((uint), rgb_hex_uchar, ));
209
210                         strcpy(bt->poin, col);
211                 }
212                 else if (bt->str[1] == ' ') {
213                         if (bt->str[0] == 'R') {
214                                 ui_but_value_set(bt, rgb[0]);
215                         }
216                         else if (bt->str[0] == 'G') {
217                                 ui_but_value_set(bt, rgb[1]);
218                         }
219                         else if (bt->str[0] == 'B') {
220                                 ui_but_value_set(bt, rgb[2]);
221                         }
222                         else if (bt->str[0] == 'H') {
223                                 ui_but_value_set(bt, hsv[0]);
224                         }
225                         else if (bt->str[0] == 'S') {
226                                 ui_but_value_set(bt, hsv[1]);
227                         }
228                         else if (bt->str[0] == 'V') {
229                                 ui_but_value_set(bt, hsv[2]);
230                         }
231                         else if (bt->str[0] == 'L') {
232                                 ui_but_value_set(bt, hsv[2]);
233                         }
234                 }
235
236                 ui_but_update(bt);
237         }
238 }
239
240 static void ui_colorpicker_rna_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
241 {
242         uiBut *but = (uiBut *)bt1;
243         uiPopupBlockHandle *popup = but->block->handle;
244         PropertyRNA *prop = but->rnaprop;
245         PointerRNA ptr = but->rnapoin;
246         float rgb[4];
247
248         if (prop) {
249                 RNA_property_float_get_array(&ptr, prop, rgb);
250                 ui_update_color_picker_buts_rgb(
251                         but, but->block, but->custom_data, rgb);
252         }
253
254         if (popup)
255                 popup->menuretval = UI_RETURN_UPDATE;
256 }
257
258 static void ui_color_wheel_rna_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
259 {
260         uiBut *but = (uiBut *)bt1;
261         uiPopupBlockHandle *popup = but->block->handle;
262         float rgb[3];
263         ColorPicker *cpicker = but->custom_data;
264         float *hsv = cpicker->color_data;
265
266         ui_color_picker_to_rgb_v(hsv, rgb);
267
268         /* hsv is saved in perceptually linear space so convert back */
269         ui_color_picker_to_scene_linear_space(but, rgb);
270
271         ui_update_color_picker_buts_rgb(but, but->block, cpicker, rgb);
272
273         if (popup)
274                 popup->menuretval = UI_RETURN_UPDATE;
275 }
276
277 static void ui_colorpicker_hex_rna_cb(bContext *UNUSED(C), void *bt1, void *hexcl)
278 {
279         uiBut *but = (uiBut *)bt1;
280         uiPopupBlockHandle *popup = but->block->handle;
281         ColorPicker *cpicker = but->custom_data;
282         char *hexcol = (char *)hexcl;
283         float rgb[3];
284
285         hex_to_rgb(hexcol, rgb, rgb + 1, rgb + 2);
286
287         /* Hex code is assumed to be in sRGB space (coming from other applications, web, etc) */
288         if (!ui_but_is_color_gamma(but)) {
289                 IMB_colormanagement_srgb_to_scene_linear_v3(rgb);
290         }
291
292         ui_update_color_picker_buts_rgb(but, but->block, cpicker, rgb);
293
294         if (popup)
295                 popup->menuretval = UI_RETURN_UPDATE;
296 }
297
298 static void ui_popup_close_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
299 {
300         uiBut *but = (uiBut *)bt1;
301         uiPopupBlockHandle *popup = but->block->handle;
302
303         if (popup) {
304                 ColorPicker *cpicker = but->custom_data;
305                 BLI_assert(cpicker->is_init);
306                 popup->menuretval = (
307                         equals_v3v3(cpicker->color_data, cpicker->color_data_init) ?
308                         UI_RETURN_CANCEL : UI_RETURN_OK);
309         }
310 }
311
312 static void ui_colorpicker_hide_reveal(uiBlock *block, enum ePickerType colormode)
313 {
314         /* tag buttons */
315         for (uiBut *bt = block->buttons.first; bt; bt = bt->next) {
316                 if ((bt->func == ui_colorpicker_rna_cb) &&
317                     (bt->type == UI_BTYPE_NUM_SLIDER) &&
318                     (bt->rnaindex != 3))
319                 {
320                         /* RGB sliders (color circle and alpha are always shown) */
321                         SET_FLAG_FROM_TEST(bt->flag, (colormode != PICKER_TYPE_RGB), UI_HIDDEN);
322                 }
323                 else if (bt->func == ui_color_wheel_rna_cb) {
324                         /* HSV sliders */
325                         SET_FLAG_FROM_TEST(bt->flag, (colormode != PICKER_TYPE_HSV), UI_HIDDEN);
326                 }
327                 else if (bt->func == ui_colorpicker_hex_rna_cb || bt->type == UI_BTYPE_LABEL) {
328                         /* HEX input or gamma correction status label */
329                         SET_FLAG_FROM_TEST(bt->flag, (colormode != PICKER_TYPE_HEX), UI_HIDDEN);
330                 }
331         }
332 }
333
334 static void ui_colorpicker_create_mode_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
335 {
336         uiBut *bt = bt1;
337         short colormode = ui_but_value_get(bt);
338         ui_colorpicker_hide_reveal(bt->block, colormode);
339 }
340
341 #define PICKER_H    (7.5f * U.widget_unit)
342 #define PICKER_W    (7.5f * U.widget_unit)
343 #define PICKER_SPACE    (0.3f * U.widget_unit)
344 #define PICKER_BAR      (0.7f * U.widget_unit)
345
346 #define PICKER_TOTAL_W  (PICKER_W + PICKER_SPACE + PICKER_BAR)
347
348 static void ui_colorpicker_circle(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, ColorPicker *cpicker)
349 {
350         uiBut *bt;
351
352         /* HS circle */
353         bt = uiDefButR_prop(
354                 block, UI_BTYPE_HSVCIRCLE, 0, "", 0, 0, PICKER_H, PICKER_W,
355                 ptr, prop, -1, 0.0, 0.0, 0.0, 0, TIP_("Color"));
356         UI_but_func_set(bt, ui_colorpicker_rna_cb, bt, NULL);
357         bt->custom_data = cpicker;
358
359         /* value */
360         if (U.color_picker_type == USER_CP_CIRCLE_HSL) {
361                 bt = uiDefButR_prop(
362                         block, UI_BTYPE_HSVCUBE, 0, "", PICKER_W + PICKER_SPACE, 0, PICKER_BAR, PICKER_H,
363                         ptr, prop, -1, 0.0, 0.0, UI_GRAD_L_ALT, 0, "Lightness");
364                 UI_but_func_set(bt, ui_colorpicker_rna_cb, bt, NULL);
365         }
366         else {
367                 bt = uiDefButR_prop(
368                         block, UI_BTYPE_HSVCUBE, 0, "", PICKER_W + PICKER_SPACE, 0, PICKER_BAR, PICKER_H,
369                         ptr, prop, -1, 0.0, 0.0, UI_GRAD_V_ALT, 0, TIP_("Value"));
370                 UI_but_func_set(bt, ui_colorpicker_rna_cb, bt, NULL);
371         }
372         bt->custom_data = cpicker;
373 }
374
375
376 static void ui_colorpicker_square(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, int type, ColorPicker *cpicker)
377 {
378         uiBut *bt;
379         int bartype = type + 3;
380
381         /* HS square */
382         bt = uiDefButR_prop(
383                 block, UI_BTYPE_HSVCUBE, 0, "",   0, PICKER_BAR + PICKER_SPACE, PICKER_TOTAL_W, PICKER_H,
384                 ptr, prop, -1, 0.0, 0.0, type, 0, TIP_("Color"));
385         UI_but_func_set(bt, ui_colorpicker_rna_cb, bt, NULL);
386         bt->custom_data = cpicker;
387
388         /* value */
389         bt = uiDefButR_prop(
390                 block, UI_BTYPE_HSVCUBE, 0, "",       0, 0, PICKER_TOTAL_W, PICKER_BAR,
391                 ptr, prop, -1, 0.0, 0.0, bartype, 0, TIP_("Value"));
392         UI_but_func_set(bt, ui_colorpicker_rna_cb, bt, NULL);
393         bt->custom_data = cpicker;
394 }
395
396 /* a HS circle, V slider, rgb/hsv/hex sliders */
397 static void ui_block_colorpicker(
398         uiBlock *block, uiBut *from_but, float rgba[4], bool show_picker)
399 {
400         /* ePickerType */
401         static char colormode = 1;
402         uiBut *bt;
403         int width, butwidth;
404         static char hexcol[128];
405         float softmin, softmax, hardmin, hardmax, step, precision;
406         int yco;
407         ColorPicker *cpicker = ui_block_colorpicker_create(block);
408         float *hsv = cpicker->color_data;
409         PointerRNA *ptr = &from_but->rnapoin;
410         PropertyRNA *prop = from_but->rnaprop;
411
412         width = PICKER_TOTAL_W;
413         butwidth = width - 1.5f * UI_UNIT_X;
414
415         /* sneaky way to check for alpha */
416         rgba[3] = FLT_MAX;
417
418         RNA_property_float_ui_range(ptr, prop, &softmin, &softmax, &step, &precision);
419         RNA_property_float_range(ptr, prop, &hardmin, &hardmax);
420         RNA_property_float_get_array(ptr, prop, rgba);
421
422         float rgb_perceptual[3];
423         copy_v3_v3(rgb_perceptual, rgba);
424         ui_scene_linear_to_color_picker_space(from_but, rgb_perceptual);
425         ui_rgb_to_color_picker_v(rgb_perceptual, hsv);
426         if (cpicker->is_init == false) {
427                 copy_v3_v3(cpicker->color_data_init, cpicker->color_data);
428                 cpicker->is_init = true;
429         }
430
431         /* when the softmax isn't defined in the RNA,
432          * using very large numbers causes sRGB/linear round trip to fail. */
433         if (softmax == FLT_MAX) {
434                 softmax = 1.0f;
435         }
436
437         switch (U.color_picker_type) {
438                 case USER_CP_SQUARE_SV:
439                         ui_colorpicker_square(block, ptr, prop, UI_GRAD_SV, cpicker);
440                         break;
441                 case USER_CP_SQUARE_HS:
442                         ui_colorpicker_square(block, ptr, prop, UI_GRAD_HS, cpicker);
443                         break;
444                 case USER_CP_SQUARE_HV:
445                         ui_colorpicker_square(block, ptr, prop, UI_GRAD_HV, cpicker);
446                         break;
447
448                 /* user default */
449                 case USER_CP_CIRCLE_HSV:
450                 case USER_CP_CIRCLE_HSL:
451                 default:
452                         ui_colorpicker_circle(block, ptr, prop, cpicker);
453                         break;
454         }
455
456         /* mode */
457         yco = -1.5f * UI_UNIT_Y;
458         UI_block_align_begin(block);
459         bt = uiDefButC(
460                 block, UI_BTYPE_ROW, 0, IFACE_("RGB"), 0, yco, width / 3, UI_UNIT_Y,
461                 &colormode, 0.0, (float)PICKER_TYPE_RGB, 0, 0, "");
462         UI_but_flag_disable(bt, UI_BUT_UNDO);
463         UI_but_func_set(bt, ui_colorpicker_create_mode_cb, bt, NULL);
464         bt->custom_data = cpicker;
465         bt = uiDefButC(
466                 block, UI_BTYPE_ROW, 0,
467                 IFACE_((U.color_picker_type == USER_CP_CIRCLE_HSL) ? "HSL" : "HSV"),
468                 width / 3, yco, width / 3, UI_UNIT_Y,
469                 &colormode, 0.0, PICKER_TYPE_HSV, 0, 0, "");
470         UI_but_flag_disable(bt, UI_BUT_UNDO);
471         UI_but_func_set(bt, ui_colorpicker_create_mode_cb, bt, NULL);
472         bt->custom_data = cpicker;
473         bt = uiDefButC(
474                 block, UI_BTYPE_ROW, 0, IFACE_("Hex"), 2 * width / 3, yco, width / 3, UI_UNIT_Y,
475                 &colormode, 0.0, PICKER_TYPE_HEX, 0, 0, "");
476         UI_but_flag_disable(bt, UI_BUT_UNDO);
477         UI_but_func_set(bt, ui_colorpicker_create_mode_cb, bt, NULL);
478         bt->custom_data = cpicker;
479         UI_block_align_end(block);
480
481         yco = -3.0f * UI_UNIT_Y;
482         if (show_picker) {
483                 bt = uiDefIconButO(
484                         block, UI_BTYPE_BUT, "UI_OT_eyedropper_color", WM_OP_INVOKE_DEFAULT, ICON_EYEDROPPER,
485                         butwidth + 10, yco, UI_UNIT_X, UI_UNIT_Y, NULL);
486                 UI_but_flag_disable(bt, UI_BUT_UNDO);
487                 UI_but_drawflag_disable(bt, UI_BUT_ICON_LEFT);
488                 UI_but_func_set(bt, ui_popup_close_cb, bt, NULL);
489                 bt->custom_data = cpicker;
490         }
491
492         /* Note: don't disable UI_BUT_UNDO for RGBA values, since these don't add undo steps. */
493
494         /* RGB values */
495         UI_block_align_begin(block);
496         bt = uiDefButR_prop(
497                 block, UI_BTYPE_NUM_SLIDER, 0, IFACE_("R:"),  0, yco, butwidth, UI_UNIT_Y,
498                 ptr, prop, 0, 0.0, 0.0, 0, 3, TIP_("Red"));
499         UI_but_func_set(bt, ui_colorpicker_rna_cb, bt, NULL);
500         bt->custom_data = cpicker;
501         bt = uiDefButR_prop(
502                 block, UI_BTYPE_NUM_SLIDER, 0, IFACE_("G:"),  0, yco -= UI_UNIT_Y, butwidth, UI_UNIT_Y,
503                 ptr, prop, 1, 0.0, 0.0, 0, 3, TIP_("Green"));
504         UI_but_func_set(bt, ui_colorpicker_rna_cb, bt, NULL);
505         bt->custom_data = cpicker;
506         bt = uiDefButR_prop(
507                 block, UI_BTYPE_NUM_SLIDER, 0, IFACE_("B:"),  0, yco -= UI_UNIT_Y, butwidth, UI_UNIT_Y,
508                 ptr, prop, 2, 0.0, 0.0, 0, 3, TIP_("Blue"));
509         UI_but_func_set(bt, ui_colorpicker_rna_cb, bt, NULL);
510         bt->custom_data = cpicker;
511
512         /* could use uiItemFullR(col, ptr, prop, -1, 0, UI_ITEM_R_EXPAND|UI_ITEM_R_SLIDER, "", ICON_NONE);
513          * but need to use UI_but_func_set for updating other fake buttons */
514
515         /* HSV values */
516         yco = -3.0f * UI_UNIT_Y;
517         UI_block_align_begin(block);
518         bt = uiDefButF(
519                 block, UI_BTYPE_NUM_SLIDER, 0, IFACE_("H:"),   0, yco, butwidth,
520                 UI_UNIT_Y, hsv, 0.0, 1.0, 10, 3, TIP_("Hue"));
521         UI_but_flag_disable(bt, UI_BUT_UNDO);
522         UI_but_func_set(bt, ui_color_wheel_rna_cb, bt, hsv);
523         bt->custom_data = cpicker;
524         bt = uiDefButF(
525                 block, UI_BTYPE_NUM_SLIDER, 0, IFACE_("S:"),   0, yco -= UI_UNIT_Y,
526                 butwidth, UI_UNIT_Y, hsv + 1, 0.0, 1.0, 10, 3, TIP_("Saturation"));
527         UI_but_flag_disable(bt, UI_BUT_UNDO);
528         UI_but_func_set(bt, ui_color_wheel_rna_cb, bt, hsv);
529         bt->custom_data = cpicker;
530         if (U.color_picker_type == USER_CP_CIRCLE_HSL) {
531                 bt = uiDefButF(
532                         block, UI_BTYPE_NUM_SLIDER, 0, IFACE_("L:"),   0, yco -= UI_UNIT_Y,
533                         butwidth, UI_UNIT_Y, hsv + 2, 0.0, 1.0, 10, 3, TIP_("Lightness"));
534         }
535         else {
536                 bt = uiDefButF(
537                         block, UI_BTYPE_NUM_SLIDER, 0, IFACE_("V:"),   0, yco -= UI_UNIT_Y, butwidth, UI_UNIT_Y,
538                         hsv + 2, 0.0, softmax, 10, 3, TIP_("Value"));
539         }
540         UI_but_flag_disable(bt, UI_BUT_UNDO);
541
542         bt->hardmax = hardmax;  /* not common but rgb  may be over 1.0 */
543         UI_but_func_set(bt, ui_color_wheel_rna_cb, bt, hsv);
544         bt->custom_data = cpicker;
545
546         UI_block_align_end(block);
547
548         if (rgba[3] != FLT_MAX) {
549                 bt = uiDefButR_prop(
550                         block, UI_BTYPE_NUM_SLIDER, 0, IFACE_("A: "),  0, yco -= UI_UNIT_Y, butwidth, UI_UNIT_Y,
551                         ptr, prop, 3, 0.0, 0.0, 0, 3, TIP_("Alpha"));
552                 UI_but_func_set(bt, ui_colorpicker_rna_cb, bt, NULL);
553                 bt->custom_data = cpicker;
554         }
555         else {
556                 rgba[3] = 1.0f;
557         }
558
559         /* Hex color is in sRGB space. */
560         float rgb_hex[3];
561         uchar rgb_hex_uchar[3];
562
563         copy_v3_v3(rgb_hex, rgba);
564
565         if (!ui_but_is_color_gamma(from_but)) {
566                 IMB_colormanagement_scene_linear_to_srgb_v3(rgb_hex);
567         }
568
569         rgb_float_to_uchar(rgb_hex_uchar, rgb_hex);
570         BLI_snprintf(hexcol, sizeof(hexcol), "%02X%02X%02X", UNPACK3_EX((uint), rgb_hex_uchar, ));
571
572         yco = -3.0f * UI_UNIT_Y;
573         bt = uiDefBut(
574                 block, UI_BTYPE_TEXT, 0, IFACE_("Hex: "), 0, yco, butwidth, UI_UNIT_Y,
575                 hexcol, 0, 8, 0, 0, TIP_("Hex triplet for color (#RRGGBB)"));
576         UI_but_flag_disable(bt, UI_BUT_UNDO);
577         UI_but_func_set(bt, ui_colorpicker_hex_rna_cb, bt, hexcol);
578         bt->custom_data = cpicker;
579         uiDefBut(
580                 block, UI_BTYPE_LABEL, 0, IFACE_("(Gamma Corrected)"), 0, yco - UI_UNIT_Y,
581                 butwidth, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
582
583         ui_colorpicker_hide_reveal(block, colormode);
584 }
585
586
587 static int ui_colorpicker_small_wheel_cb(const bContext *UNUSED(C), uiBlock *block, const wmEvent *event)
588 {
589         float add = 0.0f;
590
591         if (event->type == WHEELUPMOUSE)
592                 add = 0.05f;
593         else if (event->type == WHEELDOWNMOUSE)
594                 add = -0.05f;
595
596         if (add != 0.0f) {
597                 uiBut *but;
598
599                 for (but = block->buttons.first; but; but = but->next) {
600                         if (but->type == UI_BTYPE_HSVCUBE && but->active == NULL) {
601                                 uiPopupBlockHandle *popup = block->handle;
602                                 float rgb[3];
603                                 ColorPicker *cpicker = but->custom_data;
604                                 float *hsv = cpicker->color_data;
605
606                                 ui_but_v3_get(but, rgb);
607                                 ui_scene_linear_to_color_picker_space(but, rgb);
608                                 ui_rgb_to_color_picker_compat_v(rgb, hsv);
609
610                                 hsv[2] = clamp_f(hsv[2] + add, 0.0f, 1.0f);
611
612                                 ui_color_picker_to_rgb_v(hsv, rgb);
613                                 ui_color_picker_to_scene_linear_space(but, rgb);
614                                 ui_but_v3_set(but, rgb);
615
616                                 ui_update_color_picker_buts_rgb(but, block, cpicker, rgb);
617                                 if (popup)
618                                         popup->menuretval = UI_RETURN_UPDATE;
619
620                                 return 1;
621                         }
622                 }
623         }
624         return 0;
625 }
626
627 uiBlock *ui_block_func_COLOR(bContext *C, uiPopupBlockHandle *handle, void *arg_but)
628 {
629         uiBut *but = arg_but;
630         uiBlock *block;
631         bool show_picker = true;
632
633         block = UI_block_begin(C, handle->region, __func__, UI_EMBOSS);
634
635         if (ui_but_is_color_gamma(but)) {
636                 block->is_color_gamma_picker = true;
637         }
638
639         if (but->block) {
640                 /* if color block is invoked from a popup we wouldn't be able to set color properly
641                  * this is because color picker will close popups first and then will try to figure
642                  * out active button RNA, and of course it'll fail
643                  */
644                 show_picker = (but->block->flag & UI_BLOCK_POPUP) == 0;
645         }
646
647         copy_v3_v3(handle->retvec, but->editvec);
648
649         ui_block_colorpicker(block, but, handle->retvec, show_picker);
650
651         block->flag = UI_BLOCK_LOOP | UI_BLOCK_KEEP_OPEN | UI_BLOCK_OUT_1 | UI_BLOCK_MOVEMOUSE_QUIT;
652         UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
653         UI_block_bounds_set_normal(block, 0.5 * UI_UNIT_X);
654
655         block->block_event_func = ui_colorpicker_small_wheel_cb;
656
657         /* and lets go */
658         block->direction = UI_DIR_UP;
659
660         return block;
661 }
662
663 ColorPicker *ui_block_colorpicker_create(struct uiBlock *block)
664 {
665         ColorPicker *cpicker = MEM_callocN(sizeof(ColorPicker), "color_picker");
666         BLI_addhead(&block->color_pickers.list, cpicker);
667
668         return cpicker;
669 }
670
671 /** \} */