Blender V5.0
interface_template_color_ramp.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BKE_colorband.hh"
10#include "BKE_context.hh"
11#include "BKE_library.hh"
12
13#include "BLI_listbase.h"
14#include "BLI_rect.h"
15#include "BLI_string_ref.hh"
16
17#include "BLT_translation.hh"
18
19#include "DNA_texture_types.h"
20
21#include "ED_screen.hh"
22#include "ED_undo.hh"
23
24#include "RNA_access.hh"
25#include "RNA_prototypes.hh"
26
28#include "interface_intern.hh"
30
32
33static void colorband_flip(bContext *C, ColorBand *coba)
34{
35 CBData data_tmp[MAXCOLORBAND];
36
37 for (int a = 0; a < coba->tot; a++) {
38 data_tmp[a] = coba->data[coba->tot - (a + 1)];
39 }
40 for (int a = 0; a < coba->tot; a++) {
41 data_tmp[a].pos = 1.0f - data_tmp[a].pos;
42 coba->data[a] = data_tmp[a];
43 }
44
45 /* May as well flip the `cur`. */
46 coba->cur = coba->tot - (coba->cur + 1);
47
48 ED_undo_push(C, "Flip Color Ramp");
49}
50
51static void colorband_distribute(bContext *C, ColorBand *coba, bool evenly)
52{
53 if (coba->tot > 1) {
54 const int tot = evenly ? coba->tot - 1 : coba->tot;
55 const float gap = 1.0f / tot;
56 float pos = 0.0f;
57 for (int a = 0; a < coba->tot; a++) {
58 coba->data[a].pos = pos;
59 pos += gap;
60 }
61 const char *undo_str = evenly ? CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT,
62 "Distribute Stops Evenly") :
64 "Distribute Stops from Left");
65 ED_undo_push(C, undo_str);
66 }
67}
68
69static uiBlock *colorband_tools_fn(bContext *C, ARegion *region, void *cb_v)
70{
71 RNAUpdateCb &cb = *static_cast<RNAUpdateCb *>(cb_v);
72 const uiStyle *style = UI_style_get_dpi();
73 PointerRNA coba_ptr = RNA_property_pointer_get(&cb.ptr, cb.prop);
74 ColorBand *coba = static_cast<ColorBand *>(coba_ptr.data);
75 short yco = 0;
76 const short menuwidth = 10 * UI_UNIT_X;
77
78 uiBlock *block = UI_block_begin(C, region, __func__, blender::ui::EmbossType::Pulldown);
79
80 uiLayout &layout = blender::ui::block_layout(block,
83 0,
84 0,
86 0,
88 style);
90 {
91 layout.context_ptr_set("color_ramp", &coba_ptr);
92 }
93
94 /* We could move these to operators,
95 * although this isn't important unless we want to assign key shortcuts to them. */
96 {
97 uiBut *but = uiDefIconTextBut(block,
99 1,
100 ICON_ARROW_LEFTRIGHT,
101 IFACE_("Flip Color Ramp"),
102 0,
103 yco -= UI_UNIT_Y,
104 menuwidth,
105 UI_UNIT_Y,
106 nullptr,
107 "");
108 UI_but_func_set(but, [coba, cb](bContext &C) {
109 colorband_flip(&C, coba);
111 rna_update_cb(C, cb);
112 });
113 }
114 {
115 uiBut *but = uiDefIconTextBut(block,
117 1,
118 ICON_BLANK1,
119 IFACE_("Distribute Stops from Left"),
120 0,
121 yco -= UI_UNIT_Y,
122 menuwidth,
123 UI_UNIT_Y,
124 nullptr,
125 "");
126 UI_but_func_set(but, [coba, cb](bContext &C) {
127 colorband_distribute(&C, coba, false);
129 rna_update_cb(C, cb);
130 });
131 }
132 {
133 uiBut *but = uiDefIconTextBut(block,
135 1,
136 ICON_BLANK1,
137 IFACE_("Distribute Stops Evenly"),
138 0,
139 yco -= UI_UNIT_Y,
140 menuwidth,
141 UI_UNIT_Y,
142 nullptr,
143 "");
144 UI_but_func_set(but, [coba, cb](bContext &C) {
145 colorband_distribute(&C, coba, true);
147 rna_update_cb(C, cb);
148 });
149 }
150
151 layout.separator();
152
153 layout.op("UI_OT_eyedropper_colorramp", IFACE_("Eyedropper"), ICON_EYEDROPPER);
154
155 layout.separator();
156
157 {
158 uiBut *but = uiDefIconTextBut(block,
160 1,
161 ICON_LOOP_BACK,
162 IFACE_("Reset Color Ramp"),
163 0,
164 yco -= UI_UNIT_Y,
165 menuwidth,
166 UI_UNIT_Y,
167 nullptr,
168 "");
169 UI_but_func_set(but, [coba, cb](bContext &C) {
170 BKE_colorband_init(coba, true);
171 ED_undo_push(&C, "Reset Color Ramp");
173 rna_update_cb(C, cb);
174 });
175 }
176
178 UI_block_bounds_set_text(block, 3.0f * UI_UNIT_X);
179
180 return block;
181}
182
183static void colorband_add(bContext &C, const RNAUpdateCb &cb, ColorBand &coba)
184{
185 float pos = 0.5f;
186
187 if (coba.tot > 1) {
188 if (coba.cur > 0) {
189 pos = (coba.data[coba.cur - 1].pos + coba.data[coba.cur].pos) * 0.5f;
190 }
191 else {
192 pos = (coba.data[coba.cur + 1].pos + coba.data[coba.cur].pos) * 0.5f;
193 }
194 }
195
196 if (BKE_colorband_element_add(&coba, pos)) {
197 rna_update_cb(C, cb);
198 ED_undo_push(&C, "Add Color Ramp Stop");
199 }
200}
201
202static void colorband_update_cb(bContext * /*C*/, void *bt_v, void *coba_v)
203{
204 uiBut *bt = static_cast<uiBut *>(bt_v);
205 ColorBand *coba = static_cast<ColorBand *>(coba_v);
206
207 /* Sneaky update here, we need to sort the color-band points to be in order,
208 * however the RNA pointer then is wrong, so we update it */
210 bt->rnapoin.data = coba->data + coba->cur;
211}
212
214 uiBlock *block,
215 ColorBand *coba,
216 const rctf *butr,
217 const RNAUpdateCb &cb,
218 int expand)
219{
220 uiBut *bt;
221 const float unit = BLI_rctf_size_x(butr) / 14.0f;
222 const float xs = butr->xmin;
223 const float ys = butr->ymin;
224
225 PointerRNA ptr = RNA_pointer_create_discrete(cb.ptr.owner_id, &RNA_ColorRamp, coba);
226
227 uiLayout *split = &layout->split(0.4f, false);
228
231 uiLayout *row = &split->row(false);
232
233 bt = uiDefIconTextBut(block,
235 0,
236 ICON_ADD,
237 "",
238 0,
239 0,
240 2.0f * unit,
241 UI_UNIT_Y,
242 nullptr,
243 TIP_("Add a new color stop to the color ramp"));
244 UI_but_func_set(bt, [coba, cb](bContext &C) { colorband_add(C, cb, *coba); });
245
246 bt = uiDefIconTextBut(block,
248 0,
249 ICON_REMOVE,
250 "",
251 xs + 2.0f * unit,
252 ys + UI_UNIT_Y,
253 2.0f * unit,
254 UI_UNIT_Y,
255 nullptr,
256 TIP_("Delete the active position"));
257 UI_but_func_set(bt, [coba, cb](bContext &C) {
258 if (BKE_colorband_element_remove(coba, coba->cur)) {
259 rna_update_cb(C, cb);
260 ED_undo_push(&C, "Delete Color Ramp Stop");
261 }
262 });
263
264 RNAUpdateCb *tools_cb = MEM_new<RNAUpdateCb>(__func__, cb);
265 bt = uiDefIconBlockBut(block,
267 tools_cb,
268 0,
269 ICON_DOWNARROW_HLT,
270 xs + 4.0f * unit,
271 ys + UI_UNIT_Y,
272 2.0f * unit,
273 UI_UNIT_Y,
274 TIP_("Tools"));
275 /* Pass ownership of `tools_cb` to the button. */
277 bt,
278 [](bContext *, void *, void *) {},
279 tools_cb,
280 nullptr,
283
284 UI_block_align_end(block);
286
287 row = &split->row(false);
288
290 row->prop(&ptr, "color_mode", UI_ITEM_NONE, "", ICON_NONE);
292 row->prop(&ptr, "hue_interpolation", UI_ITEM_NONE, "", ICON_NONE);
293 }
294 else { /* COLBAND_BLEND_RGB */
295 row->prop(&ptr, "interpolation", UI_ITEM_NONE, "", ICON_NONE);
296 }
297 UI_block_align_end(block);
298
299 row = &layout->row(false);
300
301 bt = uiDefBut(
302 block, ButType::ColorBand, 0, "", xs, ys, BLI_rctf_size_x(butr), UI_UNIT_Y, coba, 0, 0, "");
303 bt->rnapoin = cb.ptr;
304 bt->rnaprop = cb.prop;
305 UI_but_func_set(bt, [cb](bContext &C) { rna_update_cb(C, cb); });
306
307 row = &layout->row(false);
308
309 if (coba->tot) {
310 CBData *cbd = coba->data + coba->cur;
311
312 ptr = RNA_pointer_create_discrete(cb.ptr.owner_id, &RNA_ColorRampElement, cbd);
313
314 if (!expand) {
315 split = &layout->split(0.3f, false);
316
317 row = &split->row(false);
318 bt = uiDefButS(block,
320 0,
321 "",
322 0,
323 0,
324 5.0f * UI_UNIT_X,
325 UI_UNIT_Y,
326 &coba->cur,
327 0.0,
328 float(std::max(0, coba->tot - 1)),
329 TIP_("Choose active color stop"));
331
332 row = &split->row(false);
333 row->prop(&ptr, "position", UI_ITEM_NONE, IFACE_("Pos"), ICON_NONE);
334
335 row = &layout->row(false);
336 row->prop(&ptr, "color", UI_ITEM_NONE, "", ICON_NONE);
337 }
338 else {
339 split = &layout->split(0.5f, false);
340 uiLayout *subsplit = &split->split(0.35f, false);
341
342 row = &subsplit->row(false);
343 bt = uiDefButS(block,
345 0,
346 "",
347 0,
348 0,
349 5.0f * UI_UNIT_X,
350 UI_UNIT_Y,
351 &coba->cur,
352 0.0,
353 float(std::max(0, coba->tot - 1)),
354 TIP_("Choose active color stop"));
356
357 row = &subsplit->row(false);
358 row->prop(&ptr, "position", UI_ITEM_R_SLIDER, IFACE_("Pos"), ICON_NONE);
359
360 row = &split->row(false);
361 row->prop(&ptr, "color", UI_ITEM_NONE, "", ICON_NONE);
362 }
363
364 /* Some special (rather awkward) treatment to update UI state on certain property changes. */
365 for (int i = block->buttons.size() - 1; i >= 0; i--) {
366 uiBut *but = block->buttons[i].get();
367 if (but->rnapoin.data != ptr.data) {
368 continue;
369 }
370 if (!but->rnaprop) {
371 continue;
372 }
373
374 const char *prop_identifier = RNA_property_identifier(but->rnaprop);
375 if (STREQ(prop_identifier, "position")) {
376 UI_but_func_set(but, colorband_update_cb, but, coba);
377 }
378
379 if (STREQ(prop_identifier, "color")) {
380 UI_but_func_set(bt, [cb](bContext &C) { rna_update_cb(C, cb); });
381 }
382 }
383 }
384}
385
388 const StringRefNull propname,
389 bool expand)
390{
391 PropertyRNA *prop = RNA_struct_find_property(ptr, propname.c_str());
392
393 if (!prop || RNA_property_type(prop) != PROP_POINTER) {
394 return;
395 }
396
397 const PointerRNA cptr = RNA_property_pointer_get(ptr, prop);
398 if (!cptr.data || !RNA_struct_is_a(cptr.type, &RNA_ColorRamp)) {
399 return;
400 }
401
402 rctf rect;
403 rect.xmin = 0;
404 rect.xmax = 10.0f * UI_UNIT_X;
405 rect.ymin = 0;
406 rect.ymax = 19.5f * UI_UNIT_X;
407
408 uiBlock *block = layout->absolute_block();
409
410 ID *id = cptr.owner_id;
412
414 layout, block, static_cast<ColorBand *>(cptr.data), &rect, RNAUpdateCb{*ptr, prop}, expand);
415
416 UI_block_lock_clear(block);
417}
#define MAXCOLORBAND
CBData * BKE_colorband_element_add(ColorBand *coba, float position)
Definition colorband.cc:606
void BKE_colorband_update_sort(ColorBand *coba)
Definition colorband.cc:584
bool BKE_colorband_element_remove(ColorBand *coba, int index)
Definition colorband.cc:632
void BKE_colorband_init(ColorBand *coba, bool rangetype)
Definition colorband.cc:23
ARegion * CTX_wm_region(const bContext *C)
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:202
#define ELEM(...)
#define STREQ(a, b)
#define CTX_N_(context, msgid)
#define TIP_(msgid)
#define BLT_I18NCONTEXT_OPERATOR_DEFAULT
#define IFACE_(msgid)
#define ID_IS_EDITABLE(_id)
Definition DNA_ID.h:705
@ COLBAND_BLEND_HSL
@ COLBAND_BLEND_HSV
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:618
void ED_undo_push(bContext *C, const char *str)
Definition ed_undo.cc:98
static void split(const char *text, const char *seps, char ***str, int *count)
@ PROP_POINTER
Definition RNA_types.hh:167
#define C
Definition RandGen.cpp:29
void UI_but_func_set(uiBut *but, std::function< void(bContext &)> func)
void * but_func_argN_copy(const void *argN)
void but_func_argN_free(void *argN)
uiBut * uiDefIconTextBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_type, int retval, int icon, blender::StringRef str, int x, int y, short width, short height, void *poin, std::optional< blender::StringRef > tip)
#define UI_UNIT_Y
void UI_block_emboss_set(uiBlock *block, blender::ui::EmbossType emboss)
uiBlock * UI_block_begin(const bContext *C, ARegion *region, std::string name, blender::ui::EmbossType emboss)
void UI_block_lock_clear(uiBlock *block)
const uiStyle * UI_style_get_dpi()
void UI_but_number_step_size_set(uiBut *but, float step_size)
uiBut * uiDefButS(uiBlock *block, ButType type, int retval, blender::StringRef str, int x, int y, short width, short height, short *poin, float min, float max, std::optional< blender::StringRef > tip)
void UI_block_bounds_set_text(uiBlock *block, int addval)
Definition interface.cc:647
void UI_block_align_begin(uiBlock *block)
void UI_block_direction_set(uiBlock *block, char direction)
#define UI_UNIT_X
uiBut * uiDefIconBlockBut(uiBlock *block, uiBlockCreateFunc func, void *arg, int retval, int icon, int x, int y, short width, short height, std::optional< blender::StringRef > tip)
void UI_but_funcN_set(uiBut *but, uiButHandleNFunc funcN, void *argN, void *arg2, uiButArgNFree func_argN_free_fn=MEM_freeN, uiButArgNCopy func_argN_copy_fn=MEM_dupallocN)
void UI_block_lock_set(uiBlock *block, bool val, const char *lockstr)
uiBut * uiDefBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_type, int retval, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, std::optional< blender::StringRef > tip)
@ UI_DIR_DOWN
void UI_block_align_end(uiBlock *block)
@ UI_ITEM_R_SLIDER
#define UI_ITEM_NONE
int64_t size() const
constexpr const char * c_str() const
uint pos
#define UI_MENU_PADDING
#define UI_MENU_WIDTH_MIN
static void colorband_flip(bContext *C, ColorBand *coba)
static void colorband_buttons_layout(uiLayout *layout, uiBlock *block, ColorBand *coba, const rctf *butr, const RNAUpdateCb &cb, int expand)
static void colorband_add(bContext &C, const RNAUpdateCb &cb, ColorBand &coba)
static void colorband_update_cb(bContext *, void *bt_v, void *coba_v)
static void colorband_distribute(bContext *C, ColorBand *coba, bool evenly)
static uiBlock * colorband_tools_fn(bContext *C, ARegion *region, void *cb_v)
void uiTemplateColorRamp(uiLayout *layout, PointerRNA *ptr, const StringRefNull propname, bool expand)
static void rna_update_cb(bContext &C, const RNAUpdateCb &cb)
#define ERROR_LIBDATA_MESSAGE
uiLayout & block_layout(uiBlock *block, LayoutDirection direction, LayoutType type, int x, int y, int size, int em, int padding, const uiStyle *style)
void block_layout_set_current(uiBlock *block, uiLayout *layout)
bool RNA_struct_is_a(const StructRNA *type, const StructRNA *srna)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
PropertyType RNA_property_type(PropertyRNA *prop)
PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
const char * RNA_property_identifier(const PropertyRNA *prop)
CBData data[32]
Definition DNA_ID.h:414
ID * owner_id
Definition RNA_types.hh:51
StructRNA * type
Definition RNA_types.hh:52
void * data
Definition RNA_types.hh:53
float xmax
float xmin
float ymax
float ymin
blender::Vector< std::unique_ptr< uiBut > > buttons
PropertyRNA * rnaprop
PointerRNA rnapoin
uiBlock * absolute_block()
void separator(float factor=1.0f, LayoutSeparatorType type=LayoutSeparatorType::Auto)
void context_ptr_set(blender::StringRef name, const PointerRNA *ptr)
uiLayout & row(bool align)
uiLayout & split(float percentage, bool align)
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, blender::wm::OpCallContext context, eUI_Item_Flag flag)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
i
Definition text_draw.cc:230
PointerRNA * ptr
Definition wm_files.cc:4238