UI: support Copy To Selected and Alt-Click for PropertyGroup members.
authorAlexander Gavrilov <angavrilov@gmail.com>
Sat, 16 Nov 2019 17:50:59 +0000 (20:50 +0300)
committerAlexander Gavrilov <angavrilov@gmail.com>
Wed, 4 Dec 2019 09:23:41 +0000 (12:23 +0300)
Rigify uses a property group to contain options of its rigs, so
currently it is impossible to use Alt-Click or Copy To Selected
to change a setting for multiple rigs at the same time.

The main problem here is that there is no efficient way to find
which bone the property group belongs to. To maintain performance,
implement this by checking the active bone if it is known. Copy
Data Path and related features still don't work, as data path
calculation can't use context.

Differential Revision: https://developer.blender.org/D6264

source/blender/blenkernel/BKE_context.h
source/blender/blenkernel/intern/context.c
source/blender/editors/interface/interface_ops.c
source/blender/makesrna/RNA_access.h
source/blender/makesrna/intern/rna_access.c

index 105f8e82343ffb8bbbf0f9e356961f9b392cf489..88a27b6796335d6ee33f4a3f3b24960236db80d3 100644 (file)
@@ -198,6 +198,9 @@ enum {
 
 PointerRNA CTX_data_pointer_get(const bContext *C, const char *member);
 PointerRNA CTX_data_pointer_get_type(const bContext *C, const char *member, StructRNA *type);
+PointerRNA CTX_data_pointer_get_type_silent(const bContext *C,
+                                            const char *member,
+                                            StructRNA *type);
 ListBase CTX_data_collection_get(const bContext *C, const char *member);
 ListBase CTX_data_dir_get_ex(const bContext *C,
                              const bool use_store,
index 2e4f756ce68f07a649d9932b66a423efb9cd39d9..e3d95bb660fe817379effcc7e1dfdbb670e0faba 100644 (file)
@@ -466,6 +466,18 @@ PointerRNA CTX_data_pointer_get_type(const bContext *C, const char *member, Stru
   return PointerRNA_NULL;
 }
 
+PointerRNA CTX_data_pointer_get_type_silent(const bContext *C, const char *member, StructRNA *type)
+{
+  PointerRNA ptr = CTX_data_pointer_get(C, member);
+
+  if (ptr.data && RNA_struct_is_a(ptr.type, type)) {
+    return ptr;
+  }
+  else {
+    return PointerRNA_NULL;
+  }
+}
+
 ListBase CTX_data_collection_get(const bContext *C, const char *member)
 {
   bContextDataResult result;
index 8af82864b037ff9685f027ad17a7e4e838260c0a..2a9ebdfaea90c063f269de99e07be64ddea592d1 100644 (file)
@@ -707,6 +707,25 @@ static void UI_OT_override_remove_button(wmOperatorType *ot)
 /** \name Copy To Selected Operator
  * \{ */
 
+#define NOT_NULL(assignment) ((assignment) != NULL)
+#define NOT_RNA_NULL(assignment) ((assignment).data != NULL)
+
+static void ui_context_selected_bones_via_pose(bContext *C, ListBase *r_lb)
+{
+  ListBase lb;
+  lb = CTX_data_collection_get(C, "selected_pose_bones");
+
+  if (!BLI_listbase_is_empty(&lb)) {
+    CollectionPointerLink *link;
+    for (link = lb.first; link; link = link->next) {
+      bPoseChannel *pchan = link->ptr.data;
+      RNA_pointer_create(link->ptr.owner_id, &RNA_Bone, pchan->bone, &link->ptr);
+    }
+  }
+
+  *r_lb = lb;
+}
+
 bool UI_context_copy_to_selected_list(bContext *C,
                                       PointerRNA *ptr,
                                       PropertyRNA *prop,
@@ -717,6 +736,52 @@ bool UI_context_copy_to_selected_list(bContext *C,
   *r_use_path_from_id = false;
   *r_path = NULL;
 
+  /* PropertyGroup objects don't have a reference to the struct that actually owns
+   * them, so it is normally necessary to do a brute force search to find it. This
+   * handles the search for non-ID owners by using the 'active' reference as a hint
+   * to preserve efficiency. Only properties defined through RNA are handled, as
+   * custom properties cannot be assumed to be valid for all instances.
+   *
+   * Properties owned by the ID are handled by the 'if (ptr->owner_id)' case below.
+   */
+  if (!RNA_property_is_idprop(prop) && RNA_struct_is_a(ptr->type, &RNA_PropertyGroup)) {
+    PointerRNA owner_ptr;
+    char *idpath = NULL;
+
+    /* First, check the active PoseBone and PoseBone->Bone. */
+    if (NOT_RNA_NULL(
+            owner_ptr = CTX_data_pointer_get_type(C, "active_pose_bone", &RNA_PoseBone))) {
+      if (NOT_NULL(idpath = RNA_path_from_struct_to_idproperty(&owner_ptr, ptr->data))) {
+        *r_lb = CTX_data_collection_get(C, "selected_pose_bones");
+      }
+      else {
+        bPoseChannel *pchan = owner_ptr.data;
+        RNA_pointer_create(owner_ptr.owner_id, &RNA_Bone, pchan->bone, &owner_ptr);
+
+        if (NOT_NULL(idpath = RNA_path_from_struct_to_idproperty(&owner_ptr, ptr->data))) {
+          ui_context_selected_bones_via_pose(C, r_lb);
+        }
+      }
+    }
+
+    if (idpath == NULL) {
+      /* Check the active EditBone if in edit mode. */
+      if (NOT_RNA_NULL(
+              owner_ptr = CTX_data_pointer_get_type_silent(C, "active_bone", &RNA_EditBone)) &&
+          NOT_NULL(idpath = RNA_path_from_struct_to_idproperty(&owner_ptr, ptr->data))) {
+        *r_lb = CTX_data_collection_get(C, "selected_editable_bones");
+      }
+
+      /* Add other simple cases here (Node, NodeSocket, Sequence, ViewLayer etc). */
+    }
+
+    if (idpath) {
+      *r_path = BLI_sprintfN("%s.%s", idpath, RNA_property_identifier(prop));
+      MEM_freeN(idpath);
+      return true;
+    }
+  }
+
   if (RNA_struct_is_a(ptr->type, &RNA_EditBone)) {
     *r_lb = CTX_data_collection_get(C, "selected_editable_bones");
   }
@@ -724,18 +789,7 @@ bool UI_context_copy_to_selected_list(bContext *C,
     *r_lb = CTX_data_collection_get(C, "selected_pose_bones");
   }
   else if (RNA_struct_is_a(ptr->type, &RNA_Bone)) {
-    ListBase lb;
-    lb = CTX_data_collection_get(C, "selected_pose_bones");
-
-    if (!BLI_listbase_is_empty(&lb)) {
-      CollectionPointerLink *link;
-      for (link = lb.first; link; link = link->next) {
-        bPoseChannel *pchan = link->ptr.data;
-        RNA_pointer_create(link->ptr.owner_id, &RNA_Bone, pchan->bone, &link->ptr);
-      }
-    }
-
-    *r_lb = lb;
+    ui_context_selected_bones_via_pose(C, r_lb);
   }
   else if (RNA_struct_is_a(ptr->type, &RNA_Sequence)) {
     *r_lb = CTX_data_collection_get(C, "selected_editable_sequences");
index 5bf6fb40c6a827b4be949da04bb1ce148c5ad6b2..7fe88ec94b7cd3c10b208dee730d8999ed2474b4 100644 (file)
@@ -40,6 +40,7 @@ struct Main;
 struct ReportList;
 struct Scene;
 struct bContext;
+struct IDProperty;
 
 /* Types */
 extern BlenderRNA BLENDER_RNA;
@@ -1156,6 +1157,8 @@ struct PropertyElemRNA {
 };
 bool RNA_path_resolve_elements(PointerRNA *ptr, const char *path, struct ListBase *r_elements);
 
+char *RNA_path_from_struct_to_idproperty(PointerRNA *ptr, struct IDProperty *needle);
+
 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);
index 78cd99837c30a0a88f680b38967f535ed908f1be..d8889455ac722b39fad933b6da6aaed27b2d5839 100644 (file)
@@ -5738,11 +5738,28 @@ static char *rna_idp_path(PointerRNA *ptr,
   return path;
 }
 
+/**
+ * Find the path from the structure referenced by the pointer to the IDProperty object.
+ *
+ * \param ptr Reference to the object owning the custom property storage.
+ * \param needle Custom property object to find.
+ * \return Relative path or NULL.
+ */
+char *RNA_path_from_struct_to_idproperty(PointerRNA *ptr, IDProperty *needle)
+{
+  IDProperty *haystack = RNA_struct_idprops(ptr, false);
+
+  if (haystack) { /* can fail when called on bones */
+    return rna_idp_path(ptr, haystack, needle, NULL);
+  }
+  else {
+    return NULL;
+  }
+}
+
 static char *rna_path_from_ID_to_idpgroup(PointerRNA *ptr)
 {
   PointerRNA id_ptr;
-  IDProperty *haystack;
-  IDProperty *needle;
 
   BLI_assert(ptr->owner_id != NULL);
 
@@ -5753,14 +5770,7 @@ static char *rna_path_from_ID_to_idpgroup(PointerRNA *ptr)
    */
   RNA_id_pointer_create(ptr->owner_id, &id_ptr);
 
-  haystack = RNA_struct_idprops(&id_ptr, false);
-  if (haystack) { /* can fail when called on bones */
-    needle = ptr->data;
-    return rna_idp_path(&id_ptr, haystack, needle, NULL);
-  }
-  else {
-    return NULL;
-  }
+  return RNA_path_from_struct_to_idproperty(&id_ptr, ptr->data);
 }
 
 /**