Blender V4.5
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
27#include "UI_interface.hh"
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 ED_undo_push(C, evenly ? "Distribute Stops Evenly" : "Distribute Stops from Left");
62 }
63}
64
65static uiBlock *colorband_tools_fn(bContext *C, ARegion *region, void *cb_v)
66{
67 RNAUpdateCb &cb = *static_cast<RNAUpdateCb *>(cb_v);
68 const uiStyle *style = UI_style_get_dpi();
69 PointerRNA coba_ptr = RNA_property_pointer_get(&cb.ptr, cb.prop);
70 ColorBand *coba = static_cast<ColorBand *>(coba_ptr.data);
71 short yco = 0;
72 const short menuwidth = 10 * UI_UNIT_X;
73
74 uiBlock *block = UI_block_begin(C, region, __func__, blender::ui::EmbossType::Pulldown);
75
76 uiLayout *layout = UI_block_layout(block,
79 0,
80 0,
82 0,
84 style);
85 UI_block_layout_set_current(block, layout);
86 {
87 uiLayoutSetContextPointer(layout, "color_ramp", &coba_ptr);
88 }
89
90 /* We could move these to operators,
91 * although this isn't important unless we want to assign key shortcuts to them. */
92 {
93 uiBut *but = uiDefIconTextBut(block,
95 1,
96 ICON_ARROW_LEFTRIGHT,
97 IFACE_("Flip Color Ramp"),
98 0,
99 yco -= UI_UNIT_Y,
100 menuwidth,
101 UI_UNIT_Y,
102 nullptr,
103 0.0,
104 0.0,
105 "");
106 UI_but_func_set(but, [coba, cb](bContext &C) {
107 colorband_flip(&C, coba);
109 rna_update_cb(C, cb);
110 });
111 }
112 {
113 uiBut *but = uiDefIconTextBut(block,
115 1,
116 ICON_BLANK1,
117 IFACE_("Distribute Stops from Left"),
118 0,
119 yco -= UI_UNIT_Y,
120 menuwidth,
121 UI_UNIT_Y,
122 nullptr,
123 0.0,
124 0.0,
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 0.0,
144 0.0,
145 "");
146 UI_but_func_set(but, [coba, cb](bContext &C) {
147 colorband_distribute(&C, coba, true);
149 rna_update_cb(C, cb);
150 });
151 }
152
153 layout->separator();
154
155 layout->op("UI_OT_eyedropper_colorramp", IFACE_("Eyedropper"), ICON_EYEDROPPER);
156
157 layout->separator();
158
159 {
160 uiBut *but = uiDefIconTextBut(block,
162 1,
163 ICON_LOOP_BACK,
164 IFACE_("Reset Color Ramp"),
165 0,
166 yco -= UI_UNIT_Y,
167 menuwidth,
168 UI_UNIT_Y,
169 nullptr,
170 0.0,
171 0.0,
172 "");
173 UI_but_func_set(but, [coba, cb](bContext &C) {
174 BKE_colorband_init(coba, true);
175 ED_undo_push(&C, "Reset Color Ramp");
177 rna_update_cb(C, cb);
178 });
179 }
180
182 UI_block_bounds_set_text(block, 3.0f * UI_UNIT_X);
183
184 return block;
185}
186
187static void colorband_add(bContext &C, const RNAUpdateCb &cb, ColorBand &coba)
188{
189 float pos = 0.5f;
190
191 if (coba.tot > 1) {
192 if (coba.cur > 0) {
193 pos = (coba.data[coba.cur - 1].pos + coba.data[coba.cur].pos) * 0.5f;
194 }
195 else {
196 pos = (coba.data[coba.cur + 1].pos + coba.data[coba.cur].pos) * 0.5f;
197 }
198 }
199
200 if (BKE_colorband_element_add(&coba, pos)) {
201 rna_update_cb(C, cb);
202 ED_undo_push(&C, "Add Color Ramp Stop");
203 }
204}
205
206static void colorband_update_cb(bContext * /*C*/, void *bt_v, void *coba_v)
207{
208 uiBut *bt = static_cast<uiBut *>(bt_v);
209 ColorBand *coba = static_cast<ColorBand *>(coba_v);
210
211 /* Sneaky update here, we need to sort the color-band points to be in order,
212 * however the RNA pointer then is wrong, so we update it */
214 bt->rnapoin.data = coba->data + coba->cur;
215}
216
218 uiBlock *block,
219 ColorBand *coba,
220 const rctf *butr,
221 const RNAUpdateCb &cb,
222 int expand)
223{
224 uiBut *bt;
225 const float unit = BLI_rctf_size_x(butr) / 14.0f;
226 const float xs = butr->xmin;
227 const float ys = butr->ymin;
228
229 PointerRNA ptr = RNA_pointer_create_discrete(cb.ptr.owner_id, &RNA_ColorRamp, coba);
230
231 uiLayout *split = &layout->split(0.4f, false);
232
235 uiLayout *row = &split->row(false);
236
237 bt = uiDefIconTextBut(block,
239 0,
240 ICON_ADD,
241 "",
242 0,
243 0,
244 2.0f * unit,
245 UI_UNIT_Y,
246 nullptr,
247 0,
248 0,
249 TIP_("Add a new color stop to the color ramp"));
250 UI_but_func_set(bt, [coba, cb](bContext &C) { colorband_add(C, cb, *coba); });
251
252 bt = uiDefIconTextBut(block,
254 0,
255 ICON_REMOVE,
256 "",
257 xs + 2.0f * unit,
258 ys + UI_UNIT_Y,
259 2.0f * unit,
260 UI_UNIT_Y,
261 nullptr,
262 0,
263 0,
264 TIP_("Delete the active position"));
265 UI_but_func_set(bt, [coba, cb](bContext &C) {
266 if (BKE_colorband_element_remove(coba, coba->cur)) {
267 rna_update_cb(C, cb);
268 ED_undo_push(&C, "Delete Color Ramp Stop");
269 }
270 });
271
272 RNAUpdateCb *tools_cb = MEM_new<RNAUpdateCb>(__func__, cb);
273 bt = uiDefIconBlockBut(block,
275 tools_cb,
276 0,
277 ICON_DOWNARROW_HLT,
278 xs + 4.0f * unit,
279 ys + UI_UNIT_Y,
280 2.0f * unit,
281 UI_UNIT_Y,
282 TIP_("Tools"));
283 /* Pass ownership of `tools_cb` to the button. */
285 bt,
286 [](bContext *, void *, void *) {},
287 tools_cb,
288 nullptr,
291
292 UI_block_align_end(block);
294
295 row = &split->row(false);
296
298 row->prop(&ptr, "color_mode", UI_ITEM_NONE, "", ICON_NONE);
300 row->prop(&ptr, "hue_interpolation", UI_ITEM_NONE, "", ICON_NONE);
301 }
302 else { /* COLBAND_BLEND_RGB */
303 row->prop(&ptr, "interpolation", UI_ITEM_NONE, "", ICON_NONE);
304 }
305 UI_block_align_end(block);
306
307 row = &layout->row(false);
308
309 bt = uiDefBut(
310 block, UI_BTYPE_COLORBAND, 0, "", xs, ys, BLI_rctf_size_x(butr), UI_UNIT_Y, coba, 0, 0, "");
311 bt->rnapoin = cb.ptr;
312 bt->rnaprop = cb.prop;
313 UI_but_func_set(bt, [cb](bContext &C) { rna_update_cb(C, cb); });
314
315 row = &layout->row(false);
316
317 if (coba->tot) {
318 CBData *cbd = coba->data + coba->cur;
319
320 ptr = RNA_pointer_create_discrete(cb.ptr.owner_id, &RNA_ColorRampElement, cbd);
321
322 if (!expand) {
323 split = &layout->split(0.3f, false);
324
325 row = &split->row(false);
326 bt = uiDefButS(block,
328 0,
329 "",
330 0,
331 0,
332 5.0f * UI_UNIT_X,
333 UI_UNIT_Y,
334 &coba->cur,
335 0.0,
336 float(std::max(0, coba->tot - 1)),
337 TIP_("Choose active color stop"));
339
340 row = &split->row(false);
341 row->prop(&ptr, "position", UI_ITEM_NONE, IFACE_("Pos"), ICON_NONE);
342
343 row = &layout->row(false);
344 row->prop(&ptr, "color", UI_ITEM_NONE, "", ICON_NONE);
345 }
346 else {
347 split = &layout->split(0.5f, false);
348 uiLayout *subsplit = &split->split(0.35f, false);
349
350 row = &subsplit->row(false);
351 bt = uiDefButS(block,
353 0,
354 "",
355 0,
356 0,
357 5.0f * UI_UNIT_X,
358 UI_UNIT_Y,
359 &coba->cur,
360 0.0,
361 float(std::max(0, coba->tot - 1)),
362 TIP_("Choose active color stop"));
364
365 row = &subsplit->row(false);
366 row->prop(&ptr, "position", UI_ITEM_R_SLIDER, IFACE_("Pos"), ICON_NONE);
367
368 row = &split->row(false);
369 row->prop(&ptr, "color", UI_ITEM_NONE, "", ICON_NONE);
370 }
371
372 /* Some special (rather awkward) treatment to update UI state on certain property changes. */
373 for (int i = block->buttons.size() - 1; i >= 0; i--) {
374 uiBut *but = block->buttons[i].get();
375 if (but->rnapoin.data != ptr.data) {
376 continue;
377 }
378 if (!but->rnaprop) {
379 continue;
380 }
381
382 const char *prop_identifier = RNA_property_identifier(but->rnaprop);
383 if (STREQ(prop_identifier, "position")) {
384 UI_but_func_set(but, colorband_update_cb, but, coba);
385 }
386
387 if (STREQ(prop_identifier, "color")) {
388 UI_but_func_set(bt, [cb](bContext &C) { rna_update_cb(C, cb); });
389 }
390 }
391 }
392}
393
396 const StringRefNull propname,
397 bool expand)
398{
399 PropertyRNA *prop = RNA_struct_find_property(ptr, propname.c_str());
400
401 if (!prop || RNA_property_type(prop) != PROP_POINTER) {
402 return;
403 }
404
405 const PointerRNA cptr = RNA_property_pointer_get(ptr, prop);
406 if (!cptr.data || !RNA_struct_is_a(cptr.type, &RNA_ColorRamp)) {
407 return;
408 }
409
410 rctf rect;
411 rect.xmin = 0;
412 rect.xmax = 10.0f * UI_UNIT_X;
413 rect.ymin = 0;
414 rect.ymax = 19.5f * UI_UNIT_X;
415
416 uiBlock *block = layout->absolute_block();
417
418 ID *id = cptr.owner_id;
420
422 layout, block, static_cast<ColorBand *>(cptr.data), &rect, RNAUpdateCb{*ptr, prop}, expand);
423
424 UI_block_lock_clear(block);
425}
#define MAXCOLORBAND
CBData * BKE_colorband_element_add(ColorBand *coba, float position)
Definition colorband.cc:605
void BKE_colorband_update_sort(ColorBand *coba)
Definition colorband.cc:583
bool BKE_colorband_element_remove(ColorBand *coba, int index)
Definition colorband.cc:631
void BKE_colorband_init(ColorBand *coba, bool rangetype)
Definition colorband.cc:22
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 TIP_(msgid)
#define IFACE_(msgid)
@ COLBAND_BLEND_HSL
@ COLBAND_BLEND_HSV
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:639
void ED_undo_push(bContext *C, const char *str)
Definition ed_undo.cc:99
static void split(const char *text, const char *seps, char ***str, int *count)
@ PROP_POINTER
Definition RNA_types.hh:155
#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)
#define UI_UNIT_Y
uiBut * uiDefIconTextBut(uiBlock *block, int type, int retval, int icon, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, std::optional< blender::StringRef > tip)
void UI_block_emboss_set(uiBlock *block, blender::ui::EmbossType emboss)
uiBut * uiDefButS(uiBlock *block, int 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)
uiBlock * UI_block_begin(const bContext *C, ARegion *region, std::string name, blender::ui::EmbossType emboss)
void UI_block_lock_clear(uiBlock *block)
uiBut * uiDefBut(uiBlock *block, int 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)
const uiStyle * UI_style_get_dpi()
void UI_but_number_step_size_set(uiBut *but, float step_size)
@ UI_DIR_DOWN
void UI_block_bounds_set_text(uiBlock *block, int addval)
Definition interface.cc:635
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)
@ UI_BTYPE_BUT
@ UI_BTYPE_COLORBAND
@ UI_BTYPE_BUT_MENU
@ UI_BTYPE_NUM
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)
void UI_block_align_end(uiBlock *block)
void uiLayoutSetContextPointer(uiLayout *layout, blender::StringRef name, PointerRNA *ptr)
@ UI_ITEM_R_SLIDER
uiLayout * UI_block_layout(uiBlock *block, int dir, int type, int x, int y, int size, int em, int padding, const uiStyle *style)
@ UI_LAYOUT_VERTICAL
@ UI_LAYOUT_MENU
#define UI_ITEM_NONE
void UI_block_layout_set_current(uiBlock *block, uiLayout *layout)
int64_t size() const
constexpr const char * c_str() const
uint pos
#define ID_IS_EDITABLE(_id)
#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
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:404
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
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, wmOperatorCallContext context, eUI_Item_Flag flag)
uiBlock * absolute_block()
void separator(float factor=1.0f, LayoutSeparatorType type=LayoutSeparatorType::Auto)
uiLayout & row(bool align)
uiLayout & split(float percentage, bool align)
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:4226