Blender V4.5
interface_template_operator_property.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_context.hh"
10#include "BKE_file_handler.hh"
11#include "BKE_idprop.hh"
12#include "BKE_screen.hh"
13
14#include "BLI_listbase.h"
15#include "BLI_string.h"
16
17#include "BLT_translation.hh"
18
20
21#include "ED_undo.hh"
22
23#include "RNA_access.hh"
24#include "RNA_prototypes.hh"
25
26#include "WM_api.hh"
27
28#include "UI_interface.hh"
29#include "interface_intern.hh"
30
31/* we may want to make this optional, disable for now. */
32// #define USE_OP_RESET_BUT
33
39
40#ifdef USE_OP_RESET_BUT
41static void ui_layout_operator_buts__reset_cb(bContext * /*C*/, void *op_pt, void * /*arg_dummy2*/)
42{
44}
45#endif
46
48 PropertyRNA *prop,
49 void *user_data)
50{
52 user_data);
53
56 {
57 return false;
58 }
59 return params->op->type->poll_property(params->C, params->op, prop);
60}
61
63 const bContext *C,
64 wmOperator *op,
65 uiLayout *layout,
66 const eButLabelAlign label_align,
67 int layout_flags)
68{
69 uiBlock *block = uiLayoutGetBlock(layout);
71
72 if (!op->properties) {
73 op->properties = blender::bke::idprop::create_group("wmOperatorProperties").release();
74 }
75
76 /* poll() on this operator may still fail,
77 * at the moment there is no nice feedback when this happens just fails silently. */
78 if (!WM_operator_repeat_check(C, op)) {
79 UI_block_lock_set(block, true, N_("Operator cannot redo"));
80 return return_info;
81 }
82
83 /* useful for macros where only one of the steps can't be re-done */
85
86 if (layout_flags & UI_TEMPLATE_OP_PROPS_SHOW_TITLE) {
87 layout->label(WM_operatortype_name(op->type, op->ptr), ICON_NONE);
88 }
89
90 /* menu */
91 if ((op->type->flag & OPTYPE_PRESET) && !(layout_flags & UI_TEMPLATE_OP_PROPS_HIDE_PRESETS)) {
92 /* XXX, no simple way to get WM_MT_operator_presets.bl_label
93 * from python! Label remains the same always! */
94 PointerRNA op_ptr;
95 uiLayout *row;
96
97 UI_block_set_active_operator(block, op, false);
98
99 row = &layout->row(true);
100 row->menu("WM_MT_operator_presets", std::nullopt, ICON_NONE);
101
102 wmOperatorType *ot = WM_operatortype_find("WM_OT_operator_preset_add", false);
103 op_ptr = op_ptr = row->op(ot, "", ICON_ADD, WM_OP_INVOKE_DEFAULT, UI_ITEM_NONE);
104 RNA_string_set(&op_ptr, "operator", op->type->idname);
105
106 op_ptr = row->op(ot, "", ICON_REMOVE, WM_OP_INVOKE_DEFAULT, UI_ITEM_NONE);
107 RNA_string_set(&op_ptr, "operator", op->type->idname);
108 RNA_boolean_set(&op_ptr, "remove_active", true);
109 }
110
111 if (op->type->ui) {
112 op->layout = layout;
113 op->type->ui((bContext *)C, op);
114 op->layout = nullptr;
115
116 /* #UI_LAYOUT_OP_SHOW_EMPTY ignored. retun_info is ignored too.
117 * We could allow #wmOperatorType.ui callback to return this, but not needed right now. */
118 }
119 else {
122 user_data.C = C;
123 user_data.op = op;
124 user_data.flag = layout_flags;
125 const bool use_prop_split = (layout_flags & UI_TEMPLATE_OP_PROPS_NO_SPLIT_LAYOUT) == 0;
126
128
129 uiLayoutSetPropSep(layout, use_prop_split);
130 uiLayoutSetPropDecorate(layout, false);
131
132 /* main draw call */
133 return_info = uiDefAutoButsRNA(
134 layout,
135 &ptr,
137 op->type->poll_property ? &user_data : nullptr,
138 op->type->prop,
139 label_align,
140 (layout_flags & UI_TEMPLATE_OP_PROPS_COMPACT));
141
142 if ((return_info & UI_PROP_BUTS_NONE_ADDED) &&
143 (layout_flags & UI_TEMPLATE_OP_PROPS_SHOW_EMPTY))
144 {
145 layout->label(IFACE_("No Properties"), ICON_NONE);
146 }
147 }
148
149#ifdef USE_OP_RESET_BUT
150 /* its possible that reset can do nothing if all have PROP_SKIP_SAVE enabled
151 * but this is not so important if this button is drawn in those cases
152 * (which isn't all that likely anyway) - campbell */
153 if (op->properties->len) {
154 uiBut *but;
155 uiLayout *col; /* needed to avoid alignment errors with previous buttons */
156
157 col = &layout->column(false);
158 block = uiLayoutGetBlock(col);
159 but = uiDefIconTextBut(block,
161 0,
162 ICON_FILE_REFRESH,
163 IFACE_("Reset"),
164 0,
165 0,
166 UI_UNIT_X,
167 UI_UNIT_Y,
168 nullptr,
169 0.0,
170 0.0,
171 0.0,
172 0.0,
173 TIP_("Reset operator defaults"));
174 UI_but_func_set(but, ui_layout_operator_buts__reset_cb, op, nullptr);
175 }
176#endif
177
178 /* set various special settings for buttons */
179
180 const bool is_popup = (block->flag & UI_BLOCK_KEEP_OPEN) != 0;
181
182 for (const std::unique_ptr<uiBut> &but : block->buttons) {
183 /* no undo for buttons for operator redo panels */
184 if (!(layout_flags & UI_TEMPLATE_OP_PROPS_ALLOW_UNDO_PUSH)) {
186 }
187
188 /* Only do this if we're not refreshing an existing UI. */
189 if (block->oldblock == nullptr) {
190 /* only for popups, see #36109. */
191
192 /* if button is operator's default property, and a text-field, enable focus for it
193 * - this is used for allowing operators with popups to rename stuff with fewer clicks
194 */
195 if (is_popup) {
196 if ((but->rnaprop == op->type->prop) && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_NUM)) {
198 }
199 }
200 }
201 }
202
203 return return_info;
204}
205
207 wmOperator *op,
208 uiLayout *layout,
209 const eButLabelAlign label_align,
210 int layout_flags,
211 bool *r_has_advanced)
212{
213 if (op->type->flag & OPTYPE_MACRO) {
214 LISTBASE_FOREACH (wmOperator *, macro_op, &op->macro) {
216 C, macro_op, layout, label_align, layout_flags, r_has_advanced);
217 }
218 }
219 else {
220 /* Might want to make label_align adjustable somehow. */
222 C, op, layout, label_align, layout_flags);
223 if (return_info & UI_PROP_BUTS_ANY_FAILED_CHECK) {
224 if (r_has_advanced) {
225 *r_has_advanced = true;
226 }
227 }
228 }
229}
230
232 wmWindowManager *wm,
233 wmOperator *op,
234 int layout_flags)
235{
236 if (op->type->flag & OPTYPE_MACRO) {
237 LISTBASE_FOREACH (wmOperator *, macro_op, &op->macro) {
238 if (!ui_layout_operator_properties_only_booleans(C, wm, macro_op, layout_flags)) {
239 return false;
240 }
241 }
242 }
243 else {
245 user_data.C = C;
246 user_data.op = op;
247 user_data.flag = layout_flags;
248
250
251 bool all_booleans = true;
252 RNA_STRUCT_BEGIN (&ptr, prop) {
253 if (RNA_property_flag(prop) & PROP_HIDDEN) {
254 continue;
255 }
256 if (op->type->poll_property &&
257 !ui_layout_operator_buts_poll_property(&ptr, prop, &user_data))
258 {
259 continue;
260 }
261 if (RNA_property_type(prop) != PROP_BOOLEAN) {
262 all_booleans = false;
263 break;
264 }
265 }
267 if (all_booleans == false) {
268 return false;
269 }
270 }
271
272 return true;
273}
274
276 const bContext *C, uiLayout *layout, wmOperator *op, eButLabelAlign label_align, short flag)
277{
279
280 /* If there are only checkbox items, don't use split layout by default. It looks weird if the
281 * check-boxes only use half the width. */
284 }
285
286 template_operator_property_buts_draw_recursive(C, op, layout, label_align, flag, nullptr);
287}
288
290{
292 uiBlock *block = uiLayoutGetBlock(layout);
293
294 if (op == nullptr) {
295 return;
296 }
297
298 /* Disable for now, doesn't fit well in popover. */
299#if 0
300 /* Repeat button with operator name as text. */
301 layout->op("SCREEN_OT_repeat_last",
302 WM_operatortype_name(op->type, op->ptr),
303 ICON_NONE,
305 0);
306#endif
307
308 if (WM_operator_repeat_check(C, op)) {
309 int layout_flags = 0;
310 if (block->panel == nullptr) {
311 layout_flags = UI_TEMPLATE_OP_PROPS_SHOW_TITLE;
312 }
313#if 0
314 bool has_advanced = false;
315#endif
316
319 C, op, layout, UI_BUT_LABEL_ALIGN_NONE, layout_flags, nullptr /* &has_advanced */);
320 /* Warning! this leaves the handle function for any other users of this block. */
321
322#if 0
323 if (has_advanced) {
324 layout->op( "SCREEN_OT_redo_last", IFACE_("More..."), ICON_NONE);
325 }
326#endif
327 }
328}
329
331{
332 /* Copied from #wm_operator_create.
333 * Create a slimmed down operator suitable only for UI drawing. */
334 wmOperator *op = MEM_callocN<wmOperator>(ot->rna_ext.srna ? __func__ : ot->idname);
335 STRNCPY(op->idname, ot->idname);
336 op->type = ot;
337
338 /* Initialize properties but do not assume ownership of them.
339 * This "minimal" operator owns nothing. */
340 op->ptr = MEM_new<PointerRNA>("wmOperatorPtrRNA");
341 op->properties = static_cast<IDProperty *>(properties->data);
342 *op->ptr = *properties;
343
344 return op;
345}
346
348 bContext *C, uiLayout *layout, const std::string &label, int index, bool valid)
349{
350 layout->label(label, ICON_NONE);
351 if (valid) {
352 uiLayout *row = &layout->row(false);
354 uiItemPopoverPanel(row, C, "WM_PT_operator_presets", "", ICON_PRESET);
355 PointerRNA op_ptr = row->op("COLLECTION_OT_exporter_export", "", ICON_EXPORT);
356 RNA_int_set(&op_ptr, "index", index);
357 }
358}
359
361 uiLayout *layout,
362 PointerRNA &exporter_ptr,
363 wmOperator *op,
364 const std::string &filename)
365{
366 uiLayout *col = &layout->column(false);
367
368 uiLayoutSetPropSep(col, true);
370
371 /* Note this property is used as an alternative to the `filepath` property of `op->ptr`.
372 * This property is a wrapper to access that property, see the `CollectionExport::filepath`
373 * code comments for details. */
374 PropertyRNA *prop = RNA_struct_find_property(&exporter_ptr, "filepath");
375
376 std::string placeholder = "//" + filename;
377 col->prop(&exporter_ptr,
378 prop,
380 0,
382 std::nullopt,
383 ICON_NONE,
384 placeholder.c_str());
385
387 op,
388 layout,
392}
393
394static void draw_exporter_item(uiList * /*ui_list*/,
395 const bContext * /*C*/,
396 uiLayout *layout,
397 PointerRNA * /*idataptr*/,
398 PointerRNA *itemptr,
399 int /*icon*/,
400 PointerRNA * /*active_dataptr*/,
401 const char * /*active_propname*/,
402 int /*index*/,
403 int /*flt_flag*/)
404{
405 uiLayout *row = &layout->row(false);
407 row->prop(itemptr, "name", UI_ITEM_NONE, "", ICON_NONE);
408}
409
411{
412 Collection *collection = CTX_data_collection(C);
413 ListBase *exporters = &collection->exporters;
414 const int index = collection->active_exporter_index;
415
416 /* Register the exporter list type on first use. */
417 static const uiListType *exporter_item_list = []() {
418 uiListType *lt = MEM_callocN<uiListType>(__func__);
419 STRNCPY(lt->idname, "COLLECTION_UL_exporter_list");
422 return lt;
423 }();
424
425 /* Draw exporter list and controls. */
426 PointerRNA collection_ptr = RNA_id_pointer_create(&collection->id);
427 uiLayout *row = &layout->row(false);
428 uiTemplateList(row,
429 C,
430 exporter_item_list->idname,
431 "",
432 &collection_ptr,
433 "exporters",
434 &collection_ptr,
435 "active_exporter_index",
436 nullptr,
437 3,
438 5,
440 1,
442
443 uiLayout *col = &row->column(true);
444 col->menu("COLLECTION_MT_exporter_add", "", ICON_ADD);
445 PointerRNA op_ptr = col->op("COLLECTION_OT_exporter_remove", "", ICON_REMOVE);
446 RNA_int_set(&op_ptr, "index", index);
447
448 col = &layout->column(true);
449 col->op("COLLECTION_OT_export_all", std::nullopt, ICON_EXPORT);
451
452 /* Draw the active exporter. */
453 CollectionExport *data = (CollectionExport *)BLI_findlink(exporters, index);
454 if (!data) {
455 return;
456 }
457
458 using namespace blender;
460 &collection->id, &RNA_CollectionExport, data);
461 PanelLayout panel = layout->panel_prop(C, &exporter_ptr, "is_open");
462
464 if (!fh) {
465 std::string label = std::string(IFACE_("Undefined")) + " " + data->fh_idname;
466 draw_export_controls(C, panel.header, label, index, false);
467 return;
468 }
469
471 if (!ot) {
472 std::string label = std::string(IFACE_("Undefined")) + " " + fh->export_operator;
473 draw_export_controls(C, panel.header, label, index, false);
474 return;
475 }
476
477 /* Assign temporary operator to uiBlock, which takes ownership. */
479 &collection->id, ot->srna, data->export_properties);
480 wmOperator *op = minimal_operator_create(ot, &properties);
482
483 /* Draw panel header and contents. */
484 std::string label(fh->label);
485 draw_export_controls(C, panel.header, label, index, true);
486 if (panel.body) {
488 C, panel.body, exporter_ptr, op, fh->get_default_filename(collection->id.name + 2));
489 }
490}
wmWindow * CTX_wm_window(const bContext *C)
Collection * CTX_data_collection(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
#define ELEM(...)
#define TIP_(msgid)
#define IFACE_(msgid)
Object groups, one object can be in many groups at once.
@ UILST_LAYOUT_DEFAULT
void ED_undo_operator_repeat_cb_evt(bContext *C, void *arg_op, int arg_unused)
Definition ed_undo.cc:712
#define RNA_STRUCT_BEGIN(sptr, prop)
#define RNA_STRUCT_END
@ PROP_BOOLEAN
Definition RNA_types.hh:150
@ PROP_HIDDEN
Definition RNA_types.hh:324
#define C
Definition RandGen.cpp:29
void UI_but_func_set(uiBut *but, std::function< void(bContext &)> func)
void UI_but_flag_disable(uiBut *but, int flag)
eAutoPropButsReturn
@ UI_PROP_BUTS_ANY_FAILED_CHECK
@ UI_PROP_BUTS_NONE_ADDED
#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_but_focus_on_enter_event(wmWindow *win, uiBut *but)
void UI_block_lock_clear(uiBlock *block)
@ UI_TEMPLATE_OP_PROPS_HIDE_ADVANCED
@ UI_TEMPLATE_OP_PROPS_SHOW_EMPTY
@ UI_TEMPLATE_OP_PROPS_ALLOW_UNDO_PUSH
@ UI_TEMPLATE_OP_PROPS_SHOW_TITLE
@ UI_TEMPLATE_OP_PROPS_HIDE_PRESETS
@ UI_TEMPLATE_OP_PROPS_COMPACT
@ UI_TEMPLATE_OP_PROPS_NO_SPLIT_LAYOUT
void UI_block_func_handle_set(uiBlock *block, uiBlockHandleFunc func, void *arg)
eButLabelAlign
@ UI_BUT_LABEL_ALIGN_NONE
eAutoPropButsReturn uiDefAutoButsRNA(uiLayout *layout, PointerRNA *ptr, bool(*check_prop)(PointerRNA *ptr, PropertyRNA *prop, void *user_data), void *user_data, PropertyRNA *prop_activate_init, eButLabelAlign label_align, bool compact)
void uiTemplateList(uiLayout *layout, const bContext *C, const char *listtype_name, const char *list_id, PointerRNA *dataptr, blender::StringRefNull propname, PointerRNA *active_dataptr, const char *active_propname, const char *item_dyntip_propname, int rows, int maxrows, int layout_type, int columns, enum uiTemplateListFlags flags)
@ UI_BLOCK_KEEP_OPEN
void UI_block_set_active_operator(uiBlock *block, wmOperator *op, const bool free)
#define UI_UNIT_X
@ UI_BTYPE_BUT
@ UI_BTYPE_TEXT
@ UI_BTYPE_NUM
@ UI_BUT_UNDO
void UI_block_lock_set(uiBlock *block, bool val, const char *lockstr)
@ UI_TEMPLATE_LIST_FLAG_NONE
void uiLayoutSetEnabled(uiLayout *layout, bool enabled)
uiBlock * uiLayoutGetBlock(uiLayout *layout)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
void uiLayoutSetEmboss(uiLayout *layout, blender::ui::EmbossType emboss)
void uiItemPopoverPanel(uiLayout *layout, const bContext *C, blender::StringRef panel_type, std::optional< blender::StringRef > name_opt, int icon)
@ OPTYPE_PRESET
Definition WM_types.hh:195
@ OPTYPE_MACRO
Definition WM_types.hh:185
#define OP_PROP_TAG_ADVANCED
Definition WM_types.hh:263
@ WM_OP_INVOKE_DEFAULT
Definition WM_types.hh:238
BMesh const char void * data
uint col
#define RNA_NO_INDEX
static bool ui_layout_operator_buts_poll_property(PointerRNA *, PropertyRNA *prop, void *user_data)
static eAutoPropButsReturn template_operator_property_buts_draw_single(const bContext *C, wmOperator *op, uiLayout *layout, const eButLabelAlign label_align, int layout_flags)
static void draw_export_properties(bContext *C, uiLayout *layout, PointerRNA &exporter_ptr, wmOperator *op, const std::string &filename)
static void draw_exporter_item(uiList *, const bContext *, uiLayout *layout, PointerRNA *, PointerRNA *itemptr, int, PointerRNA *, const char *, int, int)
void uiTemplateOperatorPropertyButs(const bContext *C, uiLayout *layout, wmOperator *op, eButLabelAlign label_align, short flag)
static void template_operator_property_buts_draw_recursive(const bContext *C, wmOperator *op, uiLayout *layout, const eButLabelAlign label_align, int layout_flags, bool *r_has_advanced)
void uiTemplateCollectionExporters(uiLayout *layout, bContext *C)
static wmOperator * minimal_operator_create(wmOperatorType *ot, PointerRNA *properties)
void uiTemplateOperatorRedoProperties(uiLayout *layout, const bContext *C)
static bool ui_layout_operator_properties_only_booleans(const bContext *C, wmWindowManager *wm, wmOperator *op, int layout_flags)
static void draw_export_controls(bContext *C, uiLayout *layout, const std::string &label, int index, bool valid)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
std::unique_ptr< IDProperty, IDPropertyDeleter > create_group(StringRef prop_name, eIDPropertyFlag flags={})
Allocate a new IDProperty of type IDP_GROUP.
FileHandlerType * file_handler_find(StringRef idname)
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
PropertyType RNA_property_type(PropertyRNA *prop)
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
int RNA_property_flag(PropertyRNA *prop)
int RNA_property_tags(PropertyRNA *prop)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
PointerRNA RNA_id_pointer_create(ID *id)
int len
Definition DNA_ID.h:165
char name[66]
Definition DNA_ID.h:415
void * data
Definition RNA_types.hh:53
std::string get_default_filename(StringRefNull name)
char export_operator[OP_MAX_TYPENAME]
blender::Vector< std::unique_ptr< uiBut > > buttons
uiBlock * oldblock
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, wmOperatorCallContext context, eUI_Item_Flag flag)
PanelLayout panel_prop(const bContext *C, PointerRNA *open_prop_owner, blender::StringRefNull open_prop_name)
void label(blender::StringRef name, int icon)
uiLayout & column(bool align)
uiLayout & row(bool align)
void menu(MenuType *mt, std::optional< blender::StringRef > name, int icon)
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)
char idname[BKE_ST_MAXNAME]
uiListDrawItemFunc draw_item
bool(* poll_property)(const bContext *C, wmOperator *op, const PropertyRNA *prop) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1092
const char * idname
Definition WM_types.hh:1032
void(* ui)(bContext *C, wmOperator *op)
Definition WM_types.hh:1097
PropertyRNA * prop
Definition WM_types.hh:1136
StructRNA * srna
Definition WM_types.hh:1124
IDProperty * properties
struct uiLayout * layout
struct wmOperatorType * type
struct PointerRNA * ptr
#define N_(msgid)
bool WM_operator_repeat_check(const bContext *, wmOperator *op)
PointerRNA * ptr
Definition wm_files.cc:4226
wmOperatorType * ot
Definition wm_files.cc:4225
std::string WM_operatortype_name(wmOperatorType *ot, PointerRNA *properties)
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
void WM_operator_properties_reset(wmOperator *op)
wmOperator * WM_operator_last_redo(const bContext *C)
bool WM_uilisttype_add(uiListType *ult)
uint8_t flag
Definition wm_window.cc:139