From ce23ae953caf9adccac4a301cbeaadcdd1f8f382 Mon Sep 17 00:00:00 2001 From: doc-code Date: Fri, 16 Aug 2024 22:56:58 +0200 Subject: [PATCH] Custom Templates Init script and blender_manifest (from previously work 'till 0.9.22) --- __init__.py | 262 ++++++++++++++++++++++++++++++++++++++++++ blender_manifest.toml | 27 +++++ 2 files changed, 289 insertions(+) create mode 100644 __init__.py create mode 100644 blender_manifest.toml diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..2499022 --- /dev/null +++ b/__init__.py @@ -0,0 +1,262 @@ +import os +import bpy +from bpy.types import AddonPreferences, PropertyGroup, Operator +from bpy.props import CollectionProperty, IntProperty, StringProperty + + +bl_info = { + "id": "custom_templates", + "name": "Custom Templates", + "tagline": "Add your own .blend files as template options for new projects", + "blender": (4, 2, 0), + "location": "File > New & File > Defaults", + "category": "System", + "support": "COMMUNITY", + "blender_manifest": "blender_manifest.toml" +} + + +class TemplateItem(PropertyGroup): + name: StringProperty( + name="Name", description="Display name for this template") + path: StringProperty( + name="Path", description="Path to the .blend file for this template", subtype='FILE_PATH') + + +class OT_SelectTemplatePopup(Operator): + bl_idname = "wm.select_template_popup" + bl_label = "Select a new custom template" + bl_description = "Create a new template occurency by selecting an existing .blend file" + + project_name: StringProperty(name="Project Name") + project_path: StringProperty(name="Project Path", subtype="FILE_PATH") + + def execute(self, context): + prefs = context.preferences.addons[__name__].preferences + for p in prefs.projects: + if p.path == self.project_path: + already_present = True + self.report( + {'WARNING'}, f'Selected file is already in the templates list as "{p.name}".') + return {'FINISHED'} + + new_project = prefs.projects.add() + new_project.name = self.project_name + new_project.path = self.project_path + self.project_name.pop() + self.project_path.pop() + self.report( + {'INFO'}, f"Template '{self.project_name}' selected and added successfully!") + + return {'FINISHED'} + + def invoke(self, context, event): + return context.window_manager.invoke_props_dialog(self) + + +class OT_AddTemplatePopup(Operator): + bl_idname = "wm.add_template_popup" + bl_label = "Use as template" + bl_description = "Use the current .blend file to create a new template occurency" + + project_name: StringProperty(name="Project Name") + + def execute(self, context): + prefs = context.preferences.addons[__name__].preferences + current_file_path = bpy.data.filepath + for p in prefs.projects: + if p.path == current_file_path: + already_present = True + self.report( + {'WARNING'}, f'Current file is already in the templates list as "{p.name}".') + return {'FINISHED'} + + if current_file_path: + new_project = prefs.projects.add() + new_project.name = self.project_name + new_project.path = current_file_path + self.report( + {'INFO'}, f"Template '{self.project_name}' added successfully!") + else: + self.report({'ERROR'}, "Current file is not saved on disk.") + + return {'FINISHED'} + + def invoke(self, context, event): + return context.window_manager.invoke_props_dialog(self) + + +class OT_AddTemplateItem(Operator): + bl_idname = "custom_templates.add" + bl_label = "Add Template" + bl_description = "Add new template" + + def execute(self, context): + prefs = context.preferences.addons[__name__].preferences + prefs.projects.add() + prefs.active_template_index = len(prefs.projects) - 1 + self.report({'INFO'}, f"Empty template added") + return {'FINISHED'} + + +class OT_RemoveTemplateItem(Operator): + bl_idname = "custom_templates.remove" + bl_label = "Remove Template" + bl_description = "Remove selected template" + + def execute(self, context): + prefs = context.preferences.addons[__name__].preferences + self.report( + {'INFO'}, f'Template "{prefs.projects[prefs.active_template_index].name}" removed{" (`"+prefs.projects[prefs.active_template_index].path+"`)" if prefs.projects[prefs.active_template_index].path != "" else "" }') + prefs.projects.remove(prefs.active_template_index) + prefs.active_template_index = min( + max(0, prefs.active_template_index - 1), len(prefs.projects) - 1) + return {'FINISHED'} + + +class OT_MoveUpTemplateItem(bpy.types.Operator): + bl_idname = "custom_templates.move_up" + bl_label = "Move Up" + bl_description = "Move the selected template up in the list" + + def execute(self, context): + prefs = context.preferences.addons[__name__].preferences + index = prefs.active_template_index + + if index > 0: + prefs.projects.move(index, index - 1) + prefs.active_template_index -= 1 + self.report({'INFO'}, f"Templates list re-ordered") + else: + self.report({'WARNING'}, "Template is already at the top") + + return {'FINISHED'} + + +class OT_MoveDownTemplateItem(bpy.types.Operator): + bl_idname = "custom_templates.move_down" + bl_label = "Move Down" + bl_description = "Move the selected template down in the list" + + def execute(self, context): + prefs = context.preferences.addons[__name__].preferences + index = prefs.active_template_index + + if index < len(prefs.projects) - 1: + prefs.projects.move(index, index + 1) + prefs.active_template_index += 1 + self.report({'INFO'}, f"Templates list re-ordered") + else: + self.report({'WARNING'}, "Template is already at the bottom") + + return {'FINISHED'} + +class OT_OpenAddonPreferences(bpy.types.Operator): + bl_idname = "custom_templates.open_preferences" + bl_label = "Open Custom Templates Preferences" + bl_description = "Open the preferences for the Custom Templates add-on" + + def execute(self, context): + bpy.ops.screen.userpref_show('INVOKE_DEFAULT') + context.preferences.active_section = 'ADDONS' + context.window_manager.addon_search = "Custom Templates" + bpy.ops.preferences.addon_expand(module="bl_ext.user_default.custom_templates") + return {'FINISHED'} + +# Add-On Preferences +class CustomTemplatesPreferences(AddonPreferences): + bl_idname = __name__ + + projects: CollectionProperty(type=TemplateItem) + active_template_index: IntProperty( + description="Index of the selected template") + + def draw(self, context): + layout = self.layout + + layout.label( + text="Here you can setup your own .blend files that will be shown in the `File > New` menu.") + layout.label( + text="You can also use `File > Defaults > Select new custom template` and `File > Defaults > Use current as new template` to update this preferences.") + + row = layout.row() + row.template_list("UI_UL_list", "custom_templates", + self, "projects", self, "active_template_index") + + col = row.column(align=True) + col.operator("custom_templates.add", icon='ADD', text="") + col.operator("custom_templates.remove", icon='REMOVE', text="") + col.operator("custom_templates.move_up", icon='TRIA_UP', text="") + col.operator("custom_templates.move_down", icon='TRIA_DOWN', text="") + + if self.projects: + project = self.projects[self.active_template_index] + layout.prop(project, "name") + layout.prop(project, "path") + + + +def draw_addon_separator(layout): + layout.separator() + layout.label(text="Custom Templates") + layout.separator() + + +def draw_new_menu(self, context): + layout = self.layout + prefs = context.preferences.addons[__name__].preferences + + if len(prefs.projects) > 0: + draw_addon_separator(layout) + + for project in prefs.projects: + layout.operator( + "wm.read_homefile", text=project.name).filepath = project.path + + +def draw_add_template(self, context): + layout = self.layout + + draw_addon_separator(layout) + + layout.operator("custom_templates.open_preferences", + text="Manage templates") + layout.operator("wm.select_template_popup", + text="Select new custom template") + if bpy.data.filepath != "": + layout.operator("wm.add_template_popup", + text="Use current as new template") + + +def register(): + bpy.utils.register_class(TemplateItem) + bpy.utils.register_class(OT_MoveUpTemplateItem) + bpy.utils.register_class(OT_MoveDownTemplateItem) + bpy.utils.register_class(OT_AddTemplateItem) + bpy.utils.register_class(OT_RemoveTemplateItem) + bpy.utils.register_class(OT_AddTemplatePopup) + bpy.utils.register_class(OT_SelectTemplatePopup) + bpy.utils.register_class(OT_OpenAddonPreferences) + bpy.utils.register_class(CustomTemplatesPreferences) + bpy.types.TOPBAR_MT_file_new.remove(draw_new_menu) + bpy.types.TOPBAR_MT_file_new.remove(draw_add_template) + bpy.types.TOPBAR_MT_file_new.append(draw_new_menu) + bpy.types.TOPBAR_MT_file_defaults.append(draw_add_template) + + +def unregister(): + bpy.utils.unregister_class(CustomTemplatesPreferences) + bpy.utils.unregister_class(OT_MoveUpTemplateItem) + bpy.utils.unregister_class(OT_MoveDownTemplateItem) + bpy.utils.unregister_class(OT_AddTemplateItem) + bpy.utils.unregister_class(OT_AddTemplatePopup) + bpy.utils.unregister_class(OT_RemoveTemplateItem) + bpy.utils.unregister_class(OT_SelectTemplatePopup) + bpy.utils.unregister_class(OT_OpenAddonPreferences) + bpy.utils.unregister_class(TemplateItem) + bpy.types.TOPBAR_MT_file_new.remove(draw_new_menu) + bpy.types.TOPBAR_MT_file_defaults.remove(draw_add_template) + + +if __name__ == "__main__": + register() diff --git a/blender_manifest.toml b/blender_manifest.toml new file mode 100644 index 0000000..3fcbaac --- /dev/null +++ b/blender_manifest.toml @@ -0,0 +1,27 @@ +schema_version = "1.0.0" +id = "custom_templates" +version = "0.9.22" +name = "Custom Templates" +tagline = "Use your own .blend files as template options for new projects" +maintainer = "Francesco Bellini " +type = "add-on" + +# Optional link to documentation, support, source files, etc +# website = "https://extensions.blender.org/add-ons/my-example-package/" + +# Optional list defined by Blender and server, see: +# https://docs.blender.org/manual/en/dev/advanced/extensions/tags.html +tags = ["System"] + +blender_version_min = "4.2.0" + +# License conforming to https://spdx.org/licenses/ (use "SPDX: prefix) +# https://docs.blender.org/manual/en/dev/advanced/extensions/licenses.html +license = [ + "SPDX:GPL-3.0-or-later", +] +# Optional: required by some licenses. +copyright = [ + "2024 Francesco Bellini", +] +