From 2e62a301d673844d7a582a04810892852a3ad984 Mon Sep 17 00:00:00 2001 From: Manuel Martin Date: Tue, 31 Jan 2023 17:15:37 +0100 Subject: [PATCH 1/9] Add copy components support --- addons/io_hubs_addon/components/operators.py | 50 +++++++++++++++++++- addons/io_hubs_addon/components/ui.py | 13 +++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/addons/io_hubs_addon/components/operators.py b/addons/io_hubs_addon/components/operators.py index 58d6a9f2..82cb5f90 100644 --- a/addons/io_hubs_addon/components/operators.py +++ b/addons/io_hubs_addon/components/operators.py @@ -5,7 +5,7 @@ from .types import PanelType, MigrationType from .utils import get_object_source, dash_to_title, has_component, add_component, remove_component, wrap_text, display_wrapped_text -from .components_registry import get_components_registry, get_components_icons +from .components_registry import get_components_registry, get_components_icons, get_component_by_name from ..preferences import get_addon_pref from .handlers import migrate_components from .gizmos import update_gizmos @@ -437,6 +437,52 @@ def split_and_prefix_report_messages(report_string): return [f"{i+1:02d} {message}" for i, message in enumerate(report_string.split("\n\n"))] +class CopyHubsComponent(Operator): + bl_idname = "wm.copy_hubs_component" + bl_label = "Copy component from active object" + + panel_type: StringProperty(name="panel_type") + component_name: StringProperty(name="component_name") + + def get_selected_bones(self, context): + selected_bones = context.selected_pose_bones if context.mode == "POSE" else context.selected_editable_bones + selected_armatures = [ + sel_ob for sel_ob in context.selected_objects + if sel_ob.type == "ARMATURE" and sel_ob is not context.active_object] + selected_objects = [] + for armature in selected_armatures: + armature_bones = armature.pose.bones if context.mode == "POSE" else armature.data.edit_bones + target_armature_bones = armature.data.bones if context.mode == "POSE" else armature.data.edit_bones + target_bones = [bone for bone in armature_bones if bone in selected_bones] + for target_bone in target_bones: + selected_objects.append(*[bone for bone in target_armature_bones if target_bone.name == bone.name]) + return selected_objects + + def execute(self, context): + src_obj = None + selected_objects = None + if self.panel_type == PanelType.OBJECT.value: + src_obj = context.active_object + selected_objects = context.selected_objects + elif self.panel_type == PanelType.BONE.value: + src_obj = context.active_bone + selected_objects = self.get_selected_bones(context) + elif self.panel_type == PanelType.MATERIAL.value: + src_obj = context.active_object.active_material + selected_objects = [ob.active_material for ob in context.selected_objects if ob.active_material is not None] + + component_class = get_component_by_name(self.component_name) + component_id = component_class.get_id() + for dest_obj in selected_objects: + if not has_component(dest_obj, self.component_name): + add_component(dest_obj, self.component_name) + + for key, value in src_obj[component_id].items(): + dest_obj[component_id][key] = value + + return {'FINISHED'} + + def register(): bpy.utils.register_class(AddHubsComponent) bpy.utils.register_class(RemoveHubsComponent) @@ -446,6 +492,7 @@ def register(): bpy.utils.register_class(ReportScroller) bpy.utils.register_class(ViewLastReport) bpy.utils.register_class(ViewReportInInfoEditor) + bpy.utils.register_class(CopyHubsComponent) bpy.types.WindowManager.hubs_report_scroll_index = IntProperty(default=0, min=0) bpy.types.WindowManager.hubs_report_scroll_percentage = IntProperty( name="Scroll Position", default=0, min=0, max=100, subtype='PERCENTAGE') @@ -462,6 +509,7 @@ def unregister(): bpy.utils.unregister_class(ReportScroller) bpy.utils.unregister_class(ViewLastReport) bpy.utils.unregister_class(ViewReportInInfoEditor) + bpy.utils.unregister_class(CopyHubsComponent) del bpy.types.WindowManager.hubs_report_scroll_index del bpy.types.WindowManager.hubs_report_scroll_percentage del bpy.types.WindowManager.hubs_report_last_title diff --git a/addons/io_hubs_addon/components/ui.py b/addons/io_hubs_addon/components/ui.py index ab8d8c1b..b1fa31e0 100644 --- a/addons/io_hubs_addon/components/ui.py +++ b/addons/io_hubs_addon/components/ui.py @@ -50,6 +50,19 @@ def draw_component(panel, context, obj, row, component_item): top_row.label(text=display_name) + if has_properties: + is_valid_object_copy = panel_type == PanelType.OBJECT and context.mode == "OBJECT" + is_valid_bone_copy = panel_type == PanelType.BONE and context.mode in ("POSE", "EDIT_ARMATURE") + is_valid_material_copy = panel_type == PanelType.MATERIAL and context.mode == "OBJECT" + if is_valid_object_copy or is_valid_bone_copy or is_valid_material_copy: + copy_component_operator = top_row.operator( + "wm.copy_hubs_component", + text="", + icon="PASTEDOWN" + ) + copy_component_operator.component_name = component_name + copy_component_operator.panel_type = panel.bl_context + if not component_class.is_dep_only(): remove_component_operator = top_row.operator( "wm.remove_hubs_component", From 9f084bf94db8bfdb1e9490fc18cb4c968052a661 Mon Sep 17 00:00:00 2001 From: Manuel Martin Date: Tue, 31 Jan 2023 17:52:10 +0100 Subject: [PATCH 2/9] Avoid active object component properties overwrite --- addons/io_hubs_addon/components/operators.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/addons/io_hubs_addon/components/operators.py b/addons/io_hubs_addon/components/operators.py index 82cb5f90..ba6eb7e3 100644 --- a/addons/io_hubs_addon/components/operators.py +++ b/addons/io_hubs_addon/components/operators.py @@ -463,13 +463,14 @@ def execute(self, context): selected_objects = None if self.panel_type == PanelType.OBJECT.value: src_obj = context.active_object - selected_objects = context.selected_objects + selected_objects = [ob for ob in context.selected_objects if ob is not src_obj] elif self.panel_type == PanelType.BONE.value: src_obj = context.active_bone selected_objects = self.get_selected_bones(context) elif self.panel_type == PanelType.MATERIAL.value: src_obj = context.active_object.active_material - selected_objects = [ob.active_material for ob in context.selected_objects if ob.active_material is not None] + selected_objects = [ob.active_material for ob in context.selected_objects + if ob.active_material is not None and ob.active_material is not src_obj] component_class = get_component_by_name(self.component_name) component_id = component_class.get_id() From b30d7c488878d4506289bfba3e7f7390816ed0a1 Mon Sep 17 00:00:00 2001 From: Manuel Martin Date: Mon, 6 Feb 2023 14:50:08 +0100 Subject: [PATCH 3/9] Enable operator register/undo --- addons/io_hubs_addon/components/operators.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/io_hubs_addon/components/operators.py b/addons/io_hubs_addon/components/operators.py index ba6eb7e3..fffc538f 100644 --- a/addons/io_hubs_addon/components/operators.py +++ b/addons/io_hubs_addon/components/operators.py @@ -440,6 +440,7 @@ def split_and_prefix_report_messages(report_string): class CopyHubsComponent(Operator): bl_idname = "wm.copy_hubs_component" bl_label = "Copy component from active object" + bl_options = {'REGISTER', 'UNDO'} panel_type: StringProperty(name="panel_type") component_name: StringProperty(name="component_name") From 29514b434bcd5afa3b2aaa270447c0fd677367a3 Mon Sep 17 00:00:00 2001 From: Manuel Martin Date: Mon, 6 Feb 2023 14:50:41 +0100 Subject: [PATCH 4/9] Allow copy bones in the same armature --- addons/io_hubs_addon/components/operators.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/addons/io_hubs_addon/components/operators.py b/addons/io_hubs_addon/components/operators.py index fffc538f..6d219851 100644 --- a/addons/io_hubs_addon/components/operators.py +++ b/addons/io_hubs_addon/components/operators.py @@ -447,9 +447,7 @@ class CopyHubsComponent(Operator): def get_selected_bones(self, context): selected_bones = context.selected_pose_bones if context.mode == "POSE" else context.selected_editable_bones - selected_armatures = [ - sel_ob for sel_ob in context.selected_objects - if sel_ob.type == "ARMATURE" and sel_ob is not context.active_object] + selected_armatures = [sel_ob for sel_ob in context.selected_objects if sel_ob.type == "ARMATURE"] selected_objects = [] for armature in selected_armatures: armature_bones = armature.pose.bones if context.mode == "POSE" else armature.data.edit_bones From 31f135a73a014dfd4f88443d31fe3356dfd40df5 Mon Sep 17 00:00:00 2001 From: Manuel Martin Date: Mon, 6 Feb 2023 14:51:20 +0100 Subject: [PATCH 5/9] Move copy enabling to poll --- addons/io_hubs_addon/components/operators.py | 8 ++++++++ addons/io_hubs_addon/components/ui.py | 19 ++++++++----------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/addons/io_hubs_addon/components/operators.py b/addons/io_hubs_addon/components/operators.py index 6d219851..b4e3fe63 100644 --- a/addons/io_hubs_addon/components/operators.py +++ b/addons/io_hubs_addon/components/operators.py @@ -445,6 +445,14 @@ class CopyHubsComponent(Operator): panel_type: StringProperty(name="panel_type") component_name: StringProperty(name="component_name") + @classmethod + def poll(cls, context): + panel = getattr(context, 'panel') + panel_type = PanelType(panel.bl_context) + return panel_type == PanelType.OBJECT and context.mode == "OBJECT" \ + or panel_type == PanelType.BONE and context.mode in ("POSE", "EDIT_ARMATURE") \ + or panel_type == PanelType.MATERIAL and context.mode == "OBJECT" + def get_selected_bones(self, context): selected_bones = context.selected_pose_bones if context.mode == "POSE" else context.selected_editable_bones selected_armatures = [sel_ob for sel_ob in context.selected_objects if sel_ob.type == "ARMATURE"] diff --git a/addons/io_hubs_addon/components/ui.py b/addons/io_hubs_addon/components/ui.py index b1fa31e0..b4b6ff97 100644 --- a/addons/io_hubs_addon/components/ui.py +++ b/addons/io_hubs_addon/components/ui.py @@ -51,17 +51,14 @@ def draw_component(panel, context, obj, row, component_item): top_row.label(text=display_name) if has_properties: - is_valid_object_copy = panel_type == PanelType.OBJECT and context.mode == "OBJECT" - is_valid_bone_copy = panel_type == PanelType.BONE and context.mode in ("POSE", "EDIT_ARMATURE") - is_valid_material_copy = panel_type == PanelType.MATERIAL and context.mode == "OBJECT" - if is_valid_object_copy or is_valid_bone_copy or is_valid_material_copy: - copy_component_operator = top_row.operator( - "wm.copy_hubs_component", - text="", - icon="PASTEDOWN" - ) - copy_component_operator.component_name = component_name - copy_component_operator.panel_type = panel.bl_context + top_row.context_pointer_set("panel", panel) + copy_component_operator = top_row.operator( + "wm.copy_hubs_component", + text="", + icon="PASTEDOWN" + ) + copy_component_operator.component_name = component_name + copy_component_operator.panel_type = panel.bl_context if not component_class.is_dep_only(): remove_component_operator = top_row.operator( From b7794b32fc298b36c23a49f1c827a46bfb6ba1a4 Mon Sep 17 00:00:00 2001 From: Manuel Martin Date: Thu, 23 Feb 2023 19:25:33 +0100 Subject: [PATCH 6/9] Rename selected_objects/src_obj to selected_hosts/src_host --- addons/io_hubs_addon/components/operators.py | 34 ++++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/addons/io_hubs_addon/components/operators.py b/addons/io_hubs_addon/components/operators.py index b4e3fe63..ef815e71 100644 --- a/addons/io_hubs_addon/components/operators.py +++ b/addons/io_hubs_addon/components/operators.py @@ -456,37 +456,37 @@ def poll(cls, context): def get_selected_bones(self, context): selected_bones = context.selected_pose_bones if context.mode == "POSE" else context.selected_editable_bones selected_armatures = [sel_ob for sel_ob in context.selected_objects if sel_ob.type == "ARMATURE"] - selected_objects = [] + selected_bones = [] for armature in selected_armatures: armature_bones = armature.pose.bones if context.mode == "POSE" else armature.data.edit_bones target_armature_bones = armature.data.bones if context.mode == "POSE" else armature.data.edit_bones target_bones = [bone for bone in armature_bones if bone in selected_bones] for target_bone in target_bones: - selected_objects.append(*[bone for bone in target_armature_bones if target_bone.name == bone.name]) - return selected_objects + selected_bones.append(*[bone for bone in target_armature_bones if target_bone.name == bone.name]) + return selected_bones def execute(self, context): - src_obj = None - selected_objects = None + src_host = None + selected_hosts = None if self.panel_type == PanelType.OBJECT.value: - src_obj = context.active_object - selected_objects = [ob for ob in context.selected_objects if ob is not src_obj] + src_host = context.active_object + selected_hosts = [ob for ob in context.selected_objects if ob is not src_host] elif self.panel_type == PanelType.BONE.value: - src_obj = context.active_bone - selected_objects = self.get_selected_bones(context) + src_host = context.active_bone + selected_hosts = self.get_selected_bones(context) elif self.panel_type == PanelType.MATERIAL.value: - src_obj = context.active_object.active_material - selected_objects = [ob.active_material for ob in context.selected_objects - if ob.active_material is not None and ob.active_material is not src_obj] + src_host = context.active_object.active_material + selected_hosts = [ob.active_material for ob in context.selected_objects + if ob.active_material is not None and ob.active_material is not src_host] component_class = get_component_by_name(self.component_name) component_id = component_class.get_id() - for dest_obj in selected_objects: - if not has_component(dest_obj, self.component_name): - add_component(dest_obj, self.component_name) + for dest_host in selected_hosts: + if not has_component(dest_host, self.component_name): + add_component(dest_host, self.component_name) - for key, value in src_obj[component_id].items(): - dest_obj[component_id][key] = value + for key, value in src_host[component_id].items(): + dest_host[component_id][key] = value return {'FINISHED'} From f22732dd9030ef096796baf881a3d05c638098ab Mon Sep 17 00:00:00 2001 From: Manuel Martin Date: Thu, 23 Feb 2023 19:57:36 +0100 Subject: [PATCH 7/9] Enable copying in any mode --- addons/io_hubs_addon/components/operators.py | 35 +++++++++++--------- addons/io_hubs_addon/components/ui.py | 1 - 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/addons/io_hubs_addon/components/operators.py b/addons/io_hubs_addon/components/operators.py index ef815e71..9bfcd431 100644 --- a/addons/io_hubs_addon/components/operators.py +++ b/addons/io_hubs_addon/components/operators.py @@ -445,39 +445,42 @@ class CopyHubsComponent(Operator): panel_type: StringProperty(name="panel_type") component_name: StringProperty(name="component_name") - @classmethod - def poll(cls, context): - panel = getattr(context, 'panel') - panel_type = PanelType(panel.bl_context) - return panel_type == PanelType.OBJECT and context.mode == "OBJECT" \ - or panel_type == PanelType.BONE and context.mode in ("POSE", "EDIT_ARMATURE") \ - or panel_type == PanelType.MATERIAL and context.mode == "OBJECT" - def get_selected_bones(self, context): selected_bones = context.selected_pose_bones if context.mode == "POSE" else context.selected_editable_bones selected_armatures = [sel_ob for sel_ob in context.selected_objects if sel_ob.type == "ARMATURE"] - selected_bones = [] + selected_hosts = [] for armature in selected_armatures: armature_bones = armature.pose.bones if context.mode == "POSE" else armature.data.edit_bones target_armature_bones = armature.data.bones if context.mode == "POSE" else armature.data.edit_bones target_bones = [bone for bone in armature_bones if bone in selected_bones] for target_bone in target_bones: - selected_bones.append(*[bone for bone in target_armature_bones if target_bone.name == bone.name]) - return selected_bones + selected_hosts.append(*[bone for bone in target_armature_bones if target_bone.name == bone.name]) + return selected_hosts + + def get_selected_hosts(self, context): + selected_hosts = [] + for host in context.selected_objects: + if host.type == "ARMATURE" and context.mode != "OBJECT": + selected_hosts.append(*self.get_selected_bones(context)) + else: + selected_hosts.append(host) + + return selected_hosts def execute(self, context): src_host = None - selected_hosts = None + selected_hosts = self.get_selected_hosts(context) if self.panel_type == PanelType.OBJECT.value: src_host = context.active_object - selected_hosts = [ob for ob in context.selected_objects if ob is not src_host] + selected_hosts = self.get_selected_hosts(context) elif self.panel_type == PanelType.BONE.value: src_host = context.active_bone - selected_hosts = self.get_selected_bones(context) + selected_hosts = self.get_selected_hosts(context) elif self.panel_type == PanelType.MATERIAL.value: src_host = context.active_object.active_material - selected_hosts = [ob.active_material for ob in context.selected_objects - if ob.active_material is not None and ob.active_material is not src_host] + selected_hosts = [ + ob.active_material for ob in context.selected_objects + if ob.active_material and ob.active_material is not None and ob.active_material is not src_host] component_class = get_component_by_name(self.component_name) component_id = component_class.get_id() diff --git a/addons/io_hubs_addon/components/ui.py b/addons/io_hubs_addon/components/ui.py index b4b6ff97..82d5effb 100644 --- a/addons/io_hubs_addon/components/ui.py +++ b/addons/io_hubs_addon/components/ui.py @@ -51,7 +51,6 @@ def draw_component(panel, context, obj, row, component_item): top_row.label(text=display_name) if has_properties: - top_row.context_pointer_set("panel", panel) copy_component_operator = top_row.operator( "wm.copy_hubs_component", text="", From 32cd1dbf0caccf6d424d4dfb96ebceb811d68c05 Mon Sep 17 00:00:00 2001 From: Manuel Martin Date: Mon, 27 Feb 2023 10:35:25 +0100 Subject: [PATCH 8/9] Disable copy for scene components --- addons/io_hubs_addon/components/operators.py | 6 ++++++ addons/io_hubs_addon/components/ui.py | 1 + 2 files changed, 7 insertions(+) diff --git a/addons/io_hubs_addon/components/operators.py b/addons/io_hubs_addon/components/operators.py index 9bfcd431..615900c8 100644 --- a/addons/io_hubs_addon/components/operators.py +++ b/addons/io_hubs_addon/components/operators.py @@ -445,6 +445,12 @@ class CopyHubsComponent(Operator): panel_type: StringProperty(name="panel_type") component_name: StringProperty(name="component_name") + @classmethod + def poll(cls, context): + panel = getattr(context, 'panel') + panel_type = PanelType(panel.bl_context) + return panel_type != PanelType.SCENE + def get_selected_bones(self, context): selected_bones = context.selected_pose_bones if context.mode == "POSE" else context.selected_editable_bones selected_armatures = [sel_ob for sel_ob in context.selected_objects if sel_ob.type == "ARMATURE"] diff --git a/addons/io_hubs_addon/components/ui.py b/addons/io_hubs_addon/components/ui.py index 82d5effb..b4b6ff97 100644 --- a/addons/io_hubs_addon/components/ui.py +++ b/addons/io_hubs_addon/components/ui.py @@ -51,6 +51,7 @@ def draw_component(panel, context, obj, row, component_item): top_row.label(text=display_name) if has_properties: + top_row.context_pointer_set("panel", panel) copy_component_operator = top_row.operator( "wm.copy_hubs_component", text="", From d50caaff26d8ecd8ce25429290c5babee1b74649 Mon Sep 17 00:00:00 2001 From: Manuel Martin Date: Mon, 27 Feb 2023 10:45:46 +0100 Subject: [PATCH 9/9] Initialize selected_host for copy to empty and fix array extension --- addons/io_hubs_addon/components/operators.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/io_hubs_addon/components/operators.py b/addons/io_hubs_addon/components/operators.py index 615900c8..cee5645d 100644 --- a/addons/io_hubs_addon/components/operators.py +++ b/addons/io_hubs_addon/components/operators.py @@ -460,14 +460,14 @@ def get_selected_bones(self, context): target_armature_bones = armature.data.bones if context.mode == "POSE" else armature.data.edit_bones target_bones = [bone for bone in armature_bones if bone in selected_bones] for target_bone in target_bones: - selected_hosts.append(*[bone for bone in target_armature_bones if target_bone.name == bone.name]) + selected_hosts.extend([bone for bone in target_armature_bones if target_bone.name == bone.name]) return selected_hosts def get_selected_hosts(self, context): selected_hosts = [] for host in context.selected_objects: if host.type == "ARMATURE" and context.mode != "OBJECT": - selected_hosts.append(*self.get_selected_bones(context)) + selected_hosts.extend(self.get_selected_bones(context)) else: selected_hosts.append(host) @@ -475,7 +475,7 @@ def get_selected_hosts(self, context): def execute(self, context): src_host = None - selected_hosts = self.get_selected_hosts(context) + selected_hosts = [] if self.panel_type == PanelType.OBJECT.value: src_host = context.active_object selected_hosts = self.get_selected_hosts(context)