Blender V5.0
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_utf8.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
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 = layout->block();
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(
105 RNA_string_set(&op_ptr, "operator", op->type->idname);
106
107 op_ptr = row->op(ot, "", ICON_REMOVE, blender::wm::OpCallContext::InvokeDefault, UI_ITEM_NONE);
108 RNA_string_set(&op_ptr, "operator", op->type->idname);
109 RNA_boolean_set(&op_ptr, "remove_active", true);
110 }
111
112 if (op->type->ui) {
113 op->layout = layout;
114 op->type->ui((bContext *)C, op);
115 op->layout = nullptr;
116
117 /* #UI_LAYOUT_OP_SHOW_EMPTY ignored. retun_info is ignored too.
118 * We could allow #wmOperatorType.ui callback to return this, but not needed right now. */
119 }
120 else {
123 user_data.C = C;
124 user_data.op = op;
125 user_data.flag = layout_flags;
126 const bool use_prop_split = (layout_flags & UI_TEMPLATE_OP_PROPS_NO_SPLIT_LAYOUT) == 0;
127
129
130 layout->use_property_split_set(use_prop_split);
131 layout->use_property_decorate_set(false);
132
133 /* main draw call */
134 return_info = uiDefAutoButsRNA(
135 layout,
136 &ptr,
138 op->type->poll_property ? &user_data : nullptr,
139 op->type->prop,
140 label_align,
141 (layout_flags & UI_TEMPLATE_OP_PROPS_COMPACT));
142
143 if ((return_info & UI_PROP_BUTS_NONE_ADDED) &&
144 (layout_flags & UI_TEMPLATE_OP_PROPS_SHOW_EMPTY))
145 {
146 layout->label(IFACE_("No Properties"), ICON_NONE);
147 }
148 }
149
150#ifdef USE_OP_RESET_BUT
151 /* its possible that reset can do nothing if all have PROP_SKIP_SAVE enabled
152 * but this is not so important if this button is drawn in those cases
153 * (which isn't all that likely anyway) - campbell */
154 if (op->properties->len) {
155 uiBut *but;
156 uiLayout *col; /* needed to avoid alignment errors with previous buttons */
157
158 col = &layout->column(false);
159 block = col->block();
160 but = uiDefIconTextBut(block,
162 0,
163 ICON_FILE_REFRESH,
164 IFACE_("Reset"),
165 0,
166 0,
167 UI_UNIT_X,
168 UI_UNIT_Y,
169 nullptr,
170 0.0,
171 0.0,
172 TIP_("Reset operator defaults"));
173 UI_but_func_set(but, ui_layout_operator_buts__reset_cb, op, nullptr);
174 }
175#endif
176
177 /* set various special settings for buttons */
178
179 const bool is_popup = (block->flag & UI_BLOCK_KEEP_OPEN) != 0;
180
181 for (const std::unique_ptr<uiBut> &but : block->buttons) {
182 /* no undo for buttons for operator redo panels */
183 if (!(layout_flags & UI_TEMPLATE_OP_PROPS_ALLOW_UNDO_PUSH)) {
185 }
186
187 /* Only do this if we're not refreshing an existing UI. */
188 if (block->oldblock == nullptr) {
189 /* only for popups, see #36109. */
190
191 /* if button is operator's default property, and a text-field, enable focus for it
192 * - this is used for allowing operators with popups to rename stuff with fewer clicks
193 */
194 if (is_popup) {
195 if ((but->rnaprop == op->type->prop) && ELEM(but->type, ButType::Text, ButType::Num)) {
197 }
198 }
199 }
200 }
201
202 return return_info;
203}
204
206 wmOperator *op,
207 uiLayout *layout,
208 const eButLabelAlign label_align,
209 int layout_flags,
210 bool *r_has_advanced)
211{
212 if (op->type->flag & OPTYPE_MACRO) {
213 LISTBASE_FOREACH (wmOperator *, macro_op, &op->macro) {
215 C, macro_op, layout, label_align, layout_flags, r_has_advanced);
216 }
217 }
218 else {
219 /* Might want to make label_align adjustable somehow. */
221 C, op, layout, label_align, layout_flags);
222 if (return_info & UI_PROP_BUTS_ANY_FAILED_CHECK) {
223 if (r_has_advanced) {
224 *r_has_advanced = true;
225 }
226 }
227 }
228}
229
231 wmWindowManager *wm,
232 wmOperator *op,
233 int layout_flags)
234{
235 if (op->type->flag & OPTYPE_MACRO) {
236 LISTBASE_FOREACH (wmOperator *, macro_op, &op->macro) {
237 if (!ui_layout_operator_properties_only_booleans(C, wm, macro_op, layout_flags)) {
238 return false;
239 }
240 }
241 }
242 else {
244 user_data.C = C;
245 user_data.op = op;
246 user_data.flag = layout_flags;
247
249
250 bool all_booleans = true;
251 RNA_STRUCT_BEGIN (&ptr, prop) {
252 if (RNA_property_flag(prop) & PROP_HIDDEN) {
253 continue;
254 }
255 if (op->type->poll_property &&
256 !ui_layout_operator_buts_poll_property(&ptr, prop, &user_data))
257 {
258 continue;
259 }
260 if (RNA_property_type(prop) != PROP_BOOLEAN) {
261 all_booleans = false;
262 break;
263 }
264 }
266 if (all_booleans == false) {
267 return false;
268 }
269 }
270
271 return true;
272}
273
275 const bContext *C, uiLayout *layout, wmOperator *op, eButLabelAlign label_align, short flag)
276{
278
279 /* If there are only checkbox items, don't use split layout by default. It looks weird if the
280 * check-boxes only use half the width. */
283 }
284
285 template_operator_property_buts_draw_recursive(C, op, layout, label_align, flag, nullptr);
286}
287
289{
291 uiBlock *block = layout->block();
292
293 if (op == nullptr) {
294 return;
295 }
296
297 /* Disable for now, doesn't fit well in popover. */
298#if 0
299 /* Repeat button with operator name as text. */
300 layout->op("SCREEN_OT_repeat_last",
301 WM_operatortype_name(op->type, op->ptr),
302 ICON_NONE,
304 0);
305#endif
306
307 if (WM_operator_repeat_check(C, op)) {
308 int layout_flags = 0;
309 if (block->panel == nullptr) {
310 layout_flags = UI_TEMPLATE_OP_PROPS_SHOW_TITLE;
311 }
312#if 0
313 bool has_advanced = false;
314#endif
315
318 C, op, layout, UI_BUT_LABEL_ALIGN_NONE, layout_flags, nullptr /* &has_advanced */);
319 /* Warning! this leaves the handle function for any other users of this block. */
320
321#if 0
322 if (has_advanced) {
323 layout->op( "SCREEN_OT_redo_last", IFACE_("More..."), ICON_NONE);
324 }
325#endif
326 }
327}
328
330{
331 /* Copied from #wm_operator_create.
332 * Create a slimmed down operator suitable only for UI drawing. */
333 wmOperator *op = MEM_callocN<wmOperator>(ot->rna_ext.srna ? __func__ : ot->idname);
334 STRNCPY_UTF8(op->idname, ot->idname);
335 op->type = ot;
336
337 /* Initialize properties but do not assume ownership of them.
338 * This "minimal" operator owns nothing. */
339 op->ptr = MEM_new<PointerRNA>("wmOperatorPtrRNA");
340 op->properties = static_cast<IDProperty *>(properties->data);
341 *op->ptr = *properties;
342
343 return op;
344}
345
347 bContext *C, uiLayout *layout, const std::string &label, int index, bool valid)
348{
349 layout->label(label, ICON_NONE);
350 if (valid) {
351 uiLayout *row = &layout->row(false);
353 row->popover(C, "WM_PT_operator_presets", "", ICON_PRESET);
354 PointerRNA op_ptr = row->op("COLLECTION_OT_exporter_export", "", ICON_EXPORT);
355 RNA_int_set(&op_ptr, "index", index);
356 }
357}
358
360 uiLayout *layout,
361 PointerRNA &exporter_ptr,
362 wmOperator *op,
363 const std::string &filename)
364{
365 uiLayout *col = &layout->column(false);
366
367 col->use_property_split_set(true);
368 col->use_property_decorate_set(false);
369
370 /* Note this property is used as an alternative to the `filepath` property of `op->ptr`.
371 * This property is a wrapper to access that property, see the `CollectionExport::filepath`
372 * code comments for details. */
373 PropertyRNA *prop = RNA_struct_find_property(&exporter_ptr, "filepath");
374
375 std::string placeholder = "//" + filename;
376 col->prop(&exporter_ptr,
377 prop,
379 0,
381 std::nullopt,
382 ICON_NONE,
383 placeholder.c_str());
384
386 op,
387 layout,
391}
392
393static void draw_exporter_item(uiList * /*ui_list*/,
394 const bContext * /*C*/,
395 uiLayout *layout,
396 PointerRNA * /*idataptr*/,
397 PointerRNA *itemptr,
398 int /*icon*/,
399 PointerRNA * /*active_dataptr*/,
400 const char * /*active_propname*/,
401 int /*index*/,
402 int /*flt_flag*/)
403{
404 uiLayout *row = &layout->row(false);
406 row->prop(itemptr, "name", UI_ITEM_NONE, "", ICON_NONE);
407}
408
410{
411 Collection *collection = CTX_data_collection(C);
412 ListBase *exporters = &collection->exporters;
413 const int index = collection->active_exporter_index;
414
415 /* Register the exporter list type on first use. */
416 static const uiListType *exporter_item_list = []() {
417 uiListType *lt = MEM_callocN<uiListType>(__func__);
418 STRNCPY_UTF8(lt->idname, "COLLECTION_UL_exporter_list");
421 return lt;
422 }();
423
424 /* Draw exporter list and controls. */
425 PointerRNA collection_ptr = RNA_id_pointer_create(&collection->id);
426 uiLayout *row = &layout->row(false);
427 uiTemplateList(row,
428 C,
429 exporter_item_list->idname,
430 "",
431 &collection_ptr,
432 "exporters",
433 &collection_ptr,
434 "active_exporter_index",
435 nullptr,
436 3,
437 5,
439 1,
441
442 uiLayout *col = &row->column(true);
443 col->menu("COLLECTION_MT_exporter_add", "", ICON_ADD);
444 PointerRNA op_ptr = col->op("COLLECTION_OT_exporter_remove", "", ICON_REMOVE);
445 RNA_int_set(&op_ptr, "index", index);
446
447 col->separator();
448 op_ptr = col->op("COLLECTION_OT_exporter_move", "", ICON_TRIA_UP);
449 RNA_enum_set(&op_ptr, "direction", -1);
450 op_ptr = col->op("COLLECTION_OT_exporter_move", "", ICON_TRIA_DOWN);
451 RNA_enum_set(&op_ptr, "direction", 1);
452
453 col = &layout->column(true);
454 col->op("COLLECTION_OT_export_all", std::nullopt, ICON_EXPORT);
455 col->enabled_set(!BLI_listbase_is_empty(exporters));
456
457 /* Draw the active exporter. */
458 CollectionExport *data = (CollectionExport *)BLI_findlink(exporters, index);
459 if (!data) {
460 return;
461 }
462
463 using namespace blender;
465 &collection->id, &RNA_CollectionExport, data);
466 PanelLayout panel = layout->panel_prop(C, &exporter_ptr, "is_open");
467
469 if (!fh) {
470 std::string label = std::string(IFACE_("Undefined")) + " " + data->fh_idname;
471 draw_export_controls(C, panel.header, label, index, false);
472 return;
473 }
474
476 if (!ot) {
477 std::string label = std::string(IFACE_("Undefined")) + " " + fh->export_operator;
478 draw_export_controls(C, panel.header, label, index, false);
479 return;
480 }
481
482 /* Assign temporary operator to uiBlock, which takes ownership. */
484 &collection->id, ot->srna, data->export_properties);
485 wmOperator *op = minimal_operator_create(ot, &properties);
486 UI_block_set_active_operator(panel.header->block(), op, true);
487
488 /* Draw panel header and contents. */
489 std::string label(fh->label);
490 draw_export_controls(C, panel.header, label, index, true);
491 if (panel.body) {
493 C, panel.body, exporter_ptr, op, fh->get_default_filename(collection->id.name + 2));
494 }
495}
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)
#define STRNCPY_UTF8(dst, src)
#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:713
#define RNA_STRUCT_BEGIN(sptr, prop)
#define RNA_STRUCT_END
@ PROP_BOOLEAN
Definition RNA_types.hh:162
@ PROP_HIDDEN
Definition RNA_types.hh:338
#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)
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)
eAutoPropButsReturn
@ UI_PROP_BUTS_ANY_FAILED_CHECK
@ UI_PROP_BUTS_NONE_ADDED
#define UI_UNIT_Y
@ UI_BLOCK_KEEP_OPEN
void UI_but_focus_on_enter_event(wmWindow *win, uiBut *but)
void UI_block_lock_clear(uiBlock *block)
@ UI_BUT_UNDO
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)
void UI_block_set_active_operator(uiBlock *block, wmOperator *op, const bool free)
#define UI_UNIT_X
@ 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_lock_set(uiBlock *block, bool val, const char *lockstr)
@ UI_TEMPLATE_LIST_FLAG_NONE
#define UI_ITEM_NONE
@ OPTYPE_PRESET
Definition WM_types.hh:195
@ OPTYPE_MACRO
Definition WM_types.hh:185
#define OP_PROP_TAG_ADVANCED
Definition WM_types.hh:266
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)
void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
PointerRNA RNA_id_pointer_create(ID *id)
int len
Definition DNA_ID.h:175
char name[258]
Definition DNA_ID.h:432
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
void use_property_decorate_set(bool is_sep)
void popover(const bContext *C, PanelType *pt, std::optional< blender::StringRef > name_opt, int icon)
PanelLayout panel_prop(const bContext *C, PointerRNA *open_prop_owner, blender::StringRefNull open_prop_name)
uiBlock * block() const
void label(blender::StringRef name, int icon)
uiLayout & column(bool align)
uiLayout & row(bool align)
void emboss_set(blender::ui::EmbossType emboss)
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, blender::wm::OpCallContext context, eUI_Item_Flag flag)
void menu(MenuType *mt, std::optional< blender::StringRef > name, int icon)
void use_property_split_set(bool value)
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:1095
const char * idname
Definition WM_types.hh:1035
void(* ui)(bContext *C, wmOperator *op)
Definition WM_types.hh:1100
PropertyRNA * prop
Definition WM_types.hh:1139
StructRNA * srna
Definition WM_types.hh:1127
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:4238
wmOperatorType * ot
Definition wm_files.cc:4237
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:145