Python API: implement an Operator callback for dynamic description.
Blender UI Layout API allows supplying parameters to operators via button definitions. If an operator behavior strongly depends on its parameters, it may be difficult to write a tooltip that covers all of its operation modes. Thus it is useful to provide a way for the operator to produce different descriptions based on the input info. Reviewers: campbellbarton Differential Revision: https://developer.blender.org/D5709
This commit is contained in:
parent
b0b24b77ff
commit
9ecbd67dfb
Notes:
blender-bot
2023-02-14 11:21:40 +01:00
Referenced by issue #69808, Python-defined nodes implementing a draw_label function causes a segmentation fault when drawn. Referenced by issue #69633, New Operator Description function alters behavior of operator_menu_enums Referenced by issue #66606, Blender crashes in LookDev/EEVEE but not in Solid/Cycles Referenced by issue #61853, Crash on Eevee render (MacOS)
|
@ -4250,7 +4250,7 @@ static uiBut *ui_def_but_operator_ptr(uiBlock *block,
|
|||
}
|
||||
}
|
||||
|
||||
if ((!tip || tip[0] == '\0') && ot && ot->srna) {
|
||||
if ((!tip || tip[0] == '\0') && ot && ot->srna && !ot->get_description) {
|
||||
tip = RNA_struct_ui_description(ot->srna);
|
||||
}
|
||||
|
||||
|
@ -6350,6 +6350,9 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...)
|
|||
else if (but->tip && but->tip[0]) {
|
||||
tmp = BLI_strdup(but->tip);
|
||||
}
|
||||
else if (but->optype && but->optype->get_description) {
|
||||
tmp = WM_operatortype_description(C, but->optype, but->opptr);
|
||||
}
|
||||
else {
|
||||
type = BUT_GET_RNA_TIP; /* Fail-safe solution... */
|
||||
}
|
||||
|
|
|
@ -932,18 +932,14 @@ static uiTooltipData *ui_tooltip_data_from_gizmo(bContext *C, wmGizmo *gz)
|
|||
NULL;
|
||||
if (gzop != NULL) {
|
||||
/* Description */
|
||||
const char *info = RNA_struct_ui_description(gzop->type->srna);
|
||||
if (!(info && info[0])) {
|
||||
info = RNA_struct_ui_name(gzop->type->srna);
|
||||
}
|
||||
char *info = WM_operatortype_description(C, gzop->type, &gzop->ptr);
|
||||
|
||||
if (info != NULL) {
|
||||
char *text = info;
|
||||
|
||||
if (info && info[0]) {
|
||||
char *text = NULL;
|
||||
if (gzop_actions[i].prefix != NULL) {
|
||||
text = BLI_sprintfN("%s: %s", gzop_actions[i].prefix, info);
|
||||
}
|
||||
else {
|
||||
text = BLI_strdup(info);
|
||||
MEM_freeN(info);
|
||||
}
|
||||
|
||||
if (text != NULL) {
|
||||
|
|
|
@ -1420,6 +1420,39 @@ static void rna_operator_cancel_cb(bContext *C, wmOperator *op)
|
|||
RNA_parameter_list_free(&list);
|
||||
}
|
||||
|
||||
static char *rna_operator_description_cb(bContext *C, wmOperatorType *ot, PointerRNA *prop_ptr)
|
||||
{
|
||||
extern FunctionRNA rna_Operator_description_func;
|
||||
|
||||
PointerRNA ptr;
|
||||
ParameterList list;
|
||||
FunctionRNA *func;
|
||||
void *ret;
|
||||
char *result;
|
||||
|
||||
RNA_pointer_create(NULL, ot->ext.srna, NULL, &ptr); /* dummy */
|
||||
func = &rna_Operator_description_func; /* RNA_struct_find_function(&ptr, "description"); */
|
||||
|
||||
RNA_parameter_list_create(&list, &ptr, func);
|
||||
RNA_parameter_set_lookup(&list, "context", &C);
|
||||
RNA_parameter_set_lookup(&list, "properties", prop_ptr);
|
||||
ot->ext.call(C, &ptr, func, &list);
|
||||
|
||||
RNA_parameter_get_lookup(&list, "result", &ret);
|
||||
result = (char *)ret;
|
||||
|
||||
if (result && result[0]) {
|
||||
result = BLI_strdup(result);
|
||||
}
|
||||
else {
|
||||
result = NULL;
|
||||
}
|
||||
|
||||
RNA_parameter_list_free(&list);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void rna_Operator_unregister(struct Main *bmain, StructRNA *type);
|
||||
|
||||
/* bpy_operator_wrap.c */
|
||||
|
@ -1437,7 +1470,7 @@ static StructRNA *rna_Operator_register(Main *bmain,
|
|||
wmOperatorType dummyot = {NULL};
|
||||
wmOperator dummyop = {NULL};
|
||||
PointerRNA dummyotr;
|
||||
int have_function[7];
|
||||
int have_function[8];
|
||||
|
||||
struct {
|
||||
char idname[OP_MAX_TYPENAME];
|
||||
|
@ -1531,6 +1564,7 @@ static StructRNA *rna_Operator_register(Main *bmain,
|
|||
dummyot.modal = (have_function[4]) ? rna_operator_modal_cb : NULL;
|
||||
dummyot.ui = (have_function[5]) ? rna_operator_draw_cb : NULL;
|
||||
dummyot.cancel = (have_function[6]) ? rna_operator_cancel_cb : NULL;
|
||||
dummyot.get_description = (have_function[7]) ? rna_operator_description_cb : NULL;
|
||||
WM_operatortype_append_ptr(BPY_RNA_operator_wrapper, (void *)&dummyot);
|
||||
|
||||
/* update while blender is running */
|
||||
|
|
|
@ -957,6 +957,18 @@ void RNA_api_operator(StructRNA *srna)
|
|||
RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_ALLOW_WRITE);
|
||||
parm = RNA_def_pointer(func, "context", "Context", "", "");
|
||||
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
|
||||
|
||||
/* description */
|
||||
func = RNA_def_function(srna, "description", NULL);
|
||||
RNA_def_function_ui_description(func, "Compute a description string that depends on parameters");
|
||||
RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_REGISTER_OPTIONAL);
|
||||
parm = RNA_def_string(func, "result", NULL, 4096, "result", "");
|
||||
RNA_def_parameter_flags(parm, PROP_THICK_WRAP, 0);
|
||||
RNA_def_function_output(func, parm);
|
||||
parm = RNA_def_pointer(func, "context", "Context", "", "");
|
||||
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
|
||||
parm = RNA_def_pointer(func, "properties", "OperatorProperties", "", "");
|
||||
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
|
||||
}
|
||||
|
||||
void RNA_api_macro(StructRNA *srna)
|
||||
|
|
|
@ -1768,7 +1768,12 @@ static int pyrna_py_to_prop(
|
|||
if (value == Py_None) {
|
||||
if ((RNA_property_flag(prop) & PROP_NEVER_NULL) == 0) {
|
||||
if (data) {
|
||||
*((char **)data) = (char *)NULL;
|
||||
if (RNA_property_flag(prop) & PROP_THICK_WRAP) {
|
||||
*(char *)data = 0;
|
||||
}
|
||||
else {
|
||||
*((char **)data) = (char *)NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
RNA_property_string_set(ptr, prop, NULL);
|
||||
|
@ -1813,7 +1818,12 @@ static int pyrna_py_to_prop(
|
|||
}
|
||||
else {
|
||||
if (data) {
|
||||
*((char **)data) = (char *)param;
|
||||
if (RNA_property_flag(prop) & PROP_THICK_WRAP) {
|
||||
BLI_strncpy((char *)data, (char *)param, RNA_property_string_maxlength(prop));
|
||||
}
|
||||
else {
|
||||
*((char **)data) = (char *)param;
|
||||
}
|
||||
}
|
||||
else {
|
||||
RNA_property_string_set_bytes(ptr, prop, param, PyBytes_Size(value));
|
||||
|
@ -1862,7 +1872,12 @@ static int pyrna_py_to_prop(
|
|||
/* XXX, this is suspect, but needed for function calls,
|
||||
* need to see if there's a better way. */
|
||||
if (data) {
|
||||
*((char **)data) = (char *)param;
|
||||
if (RNA_property_flag(prop) & PROP_THICK_WRAP) {
|
||||
BLI_strncpy((char *)data, (char *)param, RNA_property_string_maxlength(prop));
|
||||
}
|
||||
else {
|
||||
*((char **)data) = (char *)param;
|
||||
}
|
||||
}
|
||||
else {
|
||||
RNA_property_string_set(ptr, prop, param);
|
||||
|
|
|
@ -547,6 +547,9 @@ struct wmOperatorTypeMacro *WM_operatortype_macro_define(struct wmOperatorType *
|
|||
const char *idname);
|
||||
|
||||
const char *WM_operatortype_name(struct wmOperatorType *ot, struct PointerRNA *properties);
|
||||
char *WM_operatortype_description(struct bContext *C,
|
||||
struct wmOperatorType *ot,
|
||||
struct PointerRNA *properties);
|
||||
|
||||
/* wm_uilist_type.c */
|
||||
void WM_uilisttype_init(void);
|
||||
|
|
|
@ -737,6 +737,12 @@ typedef struct wmOperatorType {
|
|||
*/
|
||||
const char *(*get_name)(struct wmOperatorType *, struct PointerRNA *);
|
||||
|
||||
/**
|
||||
* Return a different description to use in the user interface, based on property values.
|
||||
* The returned string must be freed by the caller, unless NULL.
|
||||
*/
|
||||
char *(*get_description)(struct bContext *C, struct wmOperatorType *, struct PointerRNA *);
|
||||
|
||||
/** rna for properties */
|
||||
struct StructRNA *srna;
|
||||
|
||||
|
|
|
@ -595,4 +595,35 @@ const char *WM_operatortype_name(struct wmOperatorType *ot, struct PointerRNA *p
|
|||
return (name && name[0]) ? name : RNA_struct_ui_name(ot->srna);
|
||||
}
|
||||
|
||||
char *WM_operatortype_description(struct bContext *C,
|
||||
struct wmOperatorType *ot,
|
||||
struct PointerRNA *properties)
|
||||
{
|
||||
if (ot->get_description && properties) {
|
||||
char *description = ot->get_description(C, ot, properties);
|
||||
|
||||
if (description) {
|
||||
if (description[0]) {
|
||||
return description;
|
||||
}
|
||||
else {
|
||||
MEM_freeN(description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *info = RNA_struct_ui_description(ot->srna);
|
||||
|
||||
if (!(info && info[0])) {
|
||||
info = RNA_struct_ui_name(ot->srna);
|
||||
}
|
||||
|
||||
if (info && info[0]) {
|
||||
return BLI_strdup(info);
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
Loading…
Reference in New Issue