Fix logical error resolving RNA paths

Only append RNA_path_from_ID_to_struct to context attributes if those
paths resolve to ID types.

Also simplify creating RNA paths by adding utility functions:

- WM_context_path_resolve_property_full
- WM_context_path_resolve_full

Part of fix for T90723.
This commit is contained in:
Campbell Barton 2021-08-31 11:46:47 +10:00
parent aabe6e3b45
commit 3e4d720ae4
Notes: blender-bot 2023-02-14 05:37:19 +01:00
Referenced by commit 8d40d61af0, Fix T91225: Quick Favorites and shortcuts are broken for some properties
Referenced by issue #91225, Quick Favorites and shortcuts are broken for many properties.
5 changed files with 117 additions and 71 deletions

View File

@ -70,26 +70,12 @@ static IDProperty *shortcut_property_from_rna(bContext *C, uiBut *but)
/* If this returns null, we won't be able to bind shortcuts to these RNA properties.
* Support can be added at #wm_context_member_from_ptr. */
const char *member_id = WM_context_member_from_ptr(C, &but->rnapoin);
if (member_id == NULL) {
char *final_data_path = WM_context_path_resolve_property_full(
C, &but->rnapoin, but->rnaprop, -1);
if (final_data_path == NULL) {
return NULL;
}
const char *data_path = RNA_path_from_ID_to_struct(&but->rnapoin);
const char *member_id_data_path = member_id;
if (data_path) {
member_id_data_path = BLI_sprintfN("%s.%s", member_id, data_path);
MEM_freeN((void *)data_path);
}
const char *prop_id = RNA_property_identifier(but->rnaprop);
const char *final_data_path = BLI_sprintfN("%s.%s", member_id_data_path, prop_id);
if (member_id != member_id_data_path) {
MEM_freeN((void *)member_id_data_path);
}
/* Create ID property of data path, to pass to the operator. */
const IDPropertyTemplate val = {0};
IDProperty *prop = IDP_New(IDP_GROUP, &val, __func__);
@ -329,10 +315,24 @@ static void popup_add_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2))
static bool ui_but_is_user_menu_compatible(bContext *C, uiBut *but)
{
return (but->optype ||
(but->rnaprop && (RNA_property_type(but->rnaprop) == PROP_BOOLEAN) &&
(WM_context_member_from_ptr(C, &but->rnapoin) != NULL)) ||
UI_but_menutype_get(but));
bool result = false;
if (but->optype) {
result = true;
}
else if (but->rnaprop) {
if (RNA_property_type(but->rnaprop) == PROP_BOOLEAN) {
char *data_path = WM_context_path_resolve_full(C, &but->rnapoin);
if (data_path != NULL) {
MEM_freeN(data_path);
result = true;
}
}
}
else if (UI_but_menutype_get(but)) {
result = true;
}
return result;
}
static bUserMenuItem *ui_but_user_menu_find(bContext *C, uiBut *but, bUserMenu *um)
@ -343,21 +343,11 @@ static bUserMenuItem *ui_but_user_menu_find(bContext *C, uiBut *but, bUserMenu *
&um->items, but->optype, prop, but->opcontext);
}
if (but->rnaprop) {
const char *member_id = WM_context_member_from_ptr(C, &but->rnapoin);
const char *data_path = RNA_path_from_ID_to_struct(&but->rnapoin);
const char *member_id_data_path = member_id;
if (data_path) {
member_id_data_path = BLI_sprintfN("%s.%s", member_id, data_path);
}
char *member_id_data_path = WM_context_path_resolve_full(C, &but->rnapoin);
const char *prop_id = RNA_property_identifier(but->rnaprop);
bUserMenuItem *umi = (bUserMenuItem *)ED_screen_user_menu_item_find_prop(
&um->items, member_id_data_path, prop_id, but->rnaindex);
if (data_path) {
MEM_freeN((void *)data_path);
}
if (member_id != member_id_data_path) {
MEM_freeN((void *)member_id_data_path);
}
MEM_freeN(member_id_data_path);
return umi;
}
@ -412,21 +402,11 @@ static void ui_but_user_menu_add(bContext *C, uiBut *but, bUserMenu *um)
}
else if (but->rnaprop) {
/* NOTE: 'member_id' may be a path. */
const char *member_id = WM_context_member_from_ptr(C, &but->rnapoin);
const char *data_path = RNA_path_from_ID_to_struct(&but->rnapoin);
const char *member_id_data_path = member_id;
if (data_path) {
member_id_data_path = BLI_sprintfN("%s.%s", member_id, data_path);
}
char *member_id_data_path = WM_context_path_resolve_full(C, &but->rnapoin);
const char *prop_id = RNA_property_identifier(but->rnaprop);
/* NOTE: ignore 'drawstr', use property idname always. */
ED_screen_user_menu_item_add_prop(&um->items, "", member_id_data_path, prop_id, but->rnaindex);
if (data_path) {
MEM_freeN((void *)data_path);
}
if (member_id != member_id_data_path) {
MEM_freeN((void *)member_id_data_path);
}
MEM_freeN(member_id_data_path);
}
else if ((mt = UI_but_menutype_get(but))) {
ED_screen_user_menu_item_add_menu(&um->items, drawstr, mt);

View File

@ -1162,7 +1162,7 @@ char *RNA_path_from_struct_to_idproperty(PointerRNA *ptr, struct IDProperty *nee
struct ID *RNA_find_real_ID_and_path(struct Main *bmain, struct ID *id, const char **r_path);
char *RNA_path_from_ID_to_struct(PointerRNA *ptr);
char *RNA_path_from_ID_to_struct(const PointerRNA *ptr);
char *RNA_path_from_real_ID_to_struct(struct Main *bmain, PointerRNA *ptr, struct ID **r_real);
@ -1192,7 +1192,7 @@ char *RNA_path_full_property_py(struct Main *bmain,
struct PropertyRNA *prop,
int index);
char *RNA_path_struct_property_py(struct PointerRNA *ptr, struct PropertyRNA *prop, int index);
char *RNA_path_property_py(struct PointerRNA *ptr, struct PropertyRNA *prop, int index);
char *RNA_path_property_py(const struct PointerRNA *ptr, struct PropertyRNA *prop, int index);
/* Quick name based property access
*

View File

@ -5690,7 +5690,7 @@ char *RNA_path_from_struct_to_idproperty(PointerRNA *ptr, IDProperty *needle)
return NULL;
}
static char *rna_path_from_ID_to_idpgroup(PointerRNA *ptr)
static char *rna_path_from_ID_to_idpgroup(const PointerRNA *ptr)
{
PointerRNA id_ptr;
@ -5775,7 +5775,7 @@ static char *rna_prepend_real_ID_path(Main *bmain, ID *id, char *path, ID **r_re
return prefix[0] != '\0' ? BLI_strdup(prefix) : NULL;
}
char *RNA_path_from_ID_to_struct(PointerRNA *ptr)
char *RNA_path_from_ID_to_struct(const PointerRNA *ptr)
{
char *ptrpath = NULL;
@ -5786,7 +5786,7 @@ char *RNA_path_from_ID_to_struct(PointerRNA *ptr)
if (!RNA_struct_is_ID(ptr->type)) {
if (ptr->type->path) {
/* if type has a path to some ID, use it */
ptrpath = ptr->type->path(ptr);
ptrpath = ptr->type->path((PointerRNA *)ptr);
}
else if (ptr->type->nested && RNA_struct_is_ID(ptr->type->nested)) {
PointerRNA parentptr;
@ -6156,7 +6156,7 @@ char *RNA_path_struct_property_py(PointerRNA *ptr, PropertyRNA *prop, int index)
* Get the struct.property as a python representation, eg:
* some_prop[10]
*/
char *RNA_path_property_py(PointerRNA *UNUSED(ptr), PropertyRNA *prop, int index)
char *RNA_path_property_py(const PointerRNA *UNUSED(ptr), PropertyRNA *prop, int index)
{
char *ret;

View File

@ -262,8 +262,9 @@ struct wmEventHandler_Keymap *WM_event_add_keymap_handler_priority(ListBase *han
wmKeyMap *keymap,
int priority);
typedef struct wmKeyMap *(wmEventHandler_KeymapDynamicFn)(
wmWindowManager *wm, struct wmEventHandler_Keymap *handler)ATTR_WARN_UNUSED_RESULT;
typedef struct wmKeyMap *(wmEventHandler_KeymapDynamicFn)(wmWindowManager *wm,
struct wmEventHandler_Keymap *handler)
ATTR_WARN_UNUSED_RESULT;
struct wmKeyMap *WM_event_get_keymap_from_toolsystem_fallback(
struct wmWindowManager *wm, struct wmEventHandler_Keymap *handler);
@ -573,7 +574,11 @@ void WM_operator_py_idname(char *to, const char *from);
bool WM_operator_py_idname_ok_or_report(struct ReportList *reports,
const char *classname,
const char *idname);
const char *WM_context_member_from_ptr(struct bContext *C, const struct PointerRNA *ptr);
char *WM_context_path_resolve_property_full(struct bContext *C,
const PointerRNA *ptr,
PropertyRNA *prop,
int index);
char *WM_context_path_resolve_full(struct bContext *C, const PointerRNA *ptr);
/* wm_operator_type.c */
struct wmOperatorType *WM_operatortype_find(const char *idname, bool quiet);

View File

@ -55,6 +55,7 @@
#include "BLI_dial_2d.h"
#include "BLI_dynstr.h" /* For #WM_operator_pystring. */
#include "BLI_math.h"
#include "BLI_string_utils.h"
#include "BLI_utildefines.h"
#include "BKE_brush.h"
@ -347,7 +348,7 @@ bool WM_operator_pystring_abbreviate(char *str, int str_len_max)
/* return NULL if no match is found */
#if 0
static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr)
static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr, bool *r_is_id)
{
/* loop over all context items and do 2 checks
*
@ -362,6 +363,7 @@ static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr
const char *member_found = NULL;
const char *member_id = NULL;
bool member_found_is_id = false;
for (link = lb.first; link; link = link->next) {
const char *identifier = link->data;
@ -373,14 +375,15 @@ static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr
}
if (ptr->owner_id == ctx_item_ptr.owner_id) {
const bool is_id = RNA_struct_is_ID(ctx_item_ptr.type);
if ((ptr->data == ctx_item_ptr.data) && (ptr->type == ctx_item_ptr.type)) {
/* found! */
member_found = identifier;
member_found_is_id = is_id;
break;
}
else if (RNA_struct_is_ID(ctx_item_ptr.type)) {
/* we found a reference to this ID,
* so fallback to it if there is no direct reference */
if (is_id) {
/* Found a reference to this ID, so fallback to it if there is no direct reference. */
member_id = identifier;
}
}
@ -388,9 +391,11 @@ static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr
BLI_freelistN(&lb);
if (member_found) {
*r_is_id = member_found_is_id;
return member_found;
}
else if (member_id) {
*r_is_id = true;
return member_id;
}
else {
@ -402,9 +407,24 @@ static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr
/* use hard coded checks for now */
static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr)
/**
* \param: r_is_id:
* - When set to true, the returned member is an ID type.
* This is a signal that #RNA_path_from_ID_to_struct needs to be used to calculate
* the remainder of the RNA path.
* - When set to false, the returned member is not an ID type.
* In this case the context path *must* resolve to `ptr`,
* since there is no convenient way to calculate partial RNA paths.
*
* \note While the path to the ID is typically sufficient to calculate the remainder of the path,
* in practice this would cause #WM_context_path_resolve_property_full to crate a path such as:
* `object.data.bones["Bones"].use_deform` such paths are not useful for key-shortcuts,
* so this function supports returning data-paths directly to context members that aren't ID types.
*/
static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr, bool *r_is_id)
{
const char *member_id = NULL;
bool is_id = false;
if (ptr->owner_id) {
@ -414,6 +434,7 @@ static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr
PointerRNA ctx_item_ptr = CTX_data_pointer_get(C, ctx_member); \
if (ctx_item_ptr.owner_id == idptr) { \
member_id = ctx_member; \
is_id = true; \
break; \
} \
} \
@ -426,6 +447,7 @@ static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr
PointerRNA ctx_item_ptr = CTX_data_pointer_get(C, ctx_member); \
if (ctx_item_ptr.owner_id && (ID *)cast(ctx_item_ptr.owner_id) == idptr) { \
member_id = ctx_member_full; \
is_id = true; \
break; \
} \
} \
@ -530,32 +552,71 @@ static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr
# undef TEST_PTR_DATA_TYPE
}
*r_is_id = is_id;
return member_id;
}
#endif
/**
* Calculate the path to `ptr` from constex `C`, or return NULL if it can't be calculated.
*/
char *WM_context_path_resolve_property_full(bContext *C,
const PointerRNA *ptr,
PropertyRNA *prop,
int index)
{
bool is_id;
const char *member_id = wm_context_member_from_ptr(C, ptr, &is_id);
char *member_id_data_path = NULL;
if (member_id != NULL) {
if (is_id && !RNA_struct_is_ID(ptr->type)) {
char *data_path = RNA_path_from_ID_to_struct(ptr);
if (data_path != NULL) {
if (prop != NULL) {
char *prop_str = RNA_path_property_py(ptr, prop, index);
member_id_data_path = BLI_string_join_by_sep_charN('.', member_id, data_path, prop_str);
MEM_freeN(prop_str);
}
else {
member_id_data_path = BLI_string_join_by_sep_charN('.', member_id, data_path);
}
MEM_freeN(data_path);
}
}
else {
if (prop != NULL) {
char *prop_str = RNA_path_property_py(ptr, prop, index);
member_id_data_path = BLI_string_join_by_sep_charN('.', member_id, prop_str);
MEM_freeN(prop_str);
}
else {
member_id_data_path = BLI_strdup(member_id);
}
}
}
return member_id_data_path;
}
char *WM_context_path_resolve_full(bContext *C, const PointerRNA *ptr)
{
return WM_context_path_resolve_property_full(C, ptr, NULL, -1);
}
static char *wm_prop_pystring_from_context(bContext *C,
PointerRNA *ptr,
PropertyRNA *prop,
int index)
{
const char *member_id = wm_context_member_from_ptr(C, ptr);
char *member_id_data_path = WM_context_path_resolve_property_full(C, ptr, prop, index);
char *ret = NULL;
if (member_id != NULL) {
char *prop_str = RNA_path_struct_property_py(ptr, prop, index);
if (prop_str) {
ret = BLI_sprintfN("bpy.context.%s.%s", member_id, prop_str);
MEM_freeN(prop_str);
}
if (member_id_data_path != NULL) {
ret = BLI_sprintfN("bpy.context.%s", member_id_data_path);
MEM_freeN(member_id_data_path);
}
return ret;
}
const char *WM_context_member_from_ptr(bContext *C, const PointerRNA *ptr)
{
return wm_context_member_from_ptr(C, ptr);
}
char *WM_prop_pystring_assign(bContext *C, PointerRNA *ptr, PropertyRNA *prop, int index)
{
char *lhs = C ? wm_prop_pystring_from_context(C, ptr, prop, index) : NULL;