File > New > Start from, Splash Screen 5+ support, Splash menu update, code refactor/re-organized, update v to 1.5.0
This commit is contained in:
		
							parent
							
								
									5fb7fa1a9b
								
							
						
					
					
						commit
						7636f7611b
					
				| @ -12,7 +12,6 @@ This is great, but wouldn't it be nice to be able to create **your own templates | |||||||
| 
 | 
 | ||||||
| *Instead of being limited to the usual options Blender offers to start a new project (General, 2D Animation, Sculpting, VFX, Video Editing), you will be able to configure your own list of .blend files.*   | *Instead of being limited to the usual options Blender offers to start a new project (General, 2D Animation, Sculpting, VFX, Video Editing), you will be able to configure your own list of .blend files.*   | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| --- | --- | ||||||
| 
 | 
 | ||||||
| Create your own templates or find the ones that best suit your needs online.   | Create your own templates or find the ones that best suit your needs online.   | ||||||
| @ -24,6 +23,7 @@ Create your own templates or find the ones that best suit your needs online. | |||||||
| --- | --- | ||||||
| 
 | 
 | ||||||
| #### Features | #### Features | ||||||
|  | 
 | ||||||
| - Show your templates in `File > New` menu and in the Splash Screen | - Show your templates in `File > New` menu and in the Splash Screen | ||||||
| - Import/Export templates from/to JSON file | - Import/Export templates from/to JSON file | ||||||
| - Add, remove, update, reorder your templates from add-on preferences | - Add, remove, update, reorder your templates from add-on preferences | ||||||
| @ -33,7 +33,6 @@ Create your own templates or find the ones that best suit your needs online. | |||||||
| - Clear current templates | - Clear current templates | ||||||
| - Add workspace from templates *(right-click on a workspace and use `Add from Custom Templates` menu)* | - Add workspace from templates *(right-click on a workspace and use `Add from Custom Templates` menu)* | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| ##### Legacy Support | ##### Legacy Support | ||||||
| 
 | 
 | ||||||
| For previous Blender versions, you can install the [legacy version](https://projects.blender.org/Francesco-Bellini/custom_templates_addon/wiki/Support#legacy-version).   | For previous Blender versions, you can install the [legacy version](https://projects.blender.org/Francesco-Bellini/custom_templates_addon/wiki/Support#legacy-version).   | ||||||
| @ -45,4 +44,4 @@ For previous Blender versions, you can install the [legacy version](https://proj | |||||||
| 
 | 
 | ||||||
| --- | --- | ||||||
| 
 | 
 | ||||||
| #### Make your new projects easier from the start | #### Make your new projects easier from the start | ||||||
|  | |||||||
| @ -14,9 +14,12 @@ | |||||||
| # You should have received a copy of the GNU General Public License | # You should have received a copy of the GNU General Public License | ||||||
| # along with this program.  If not, see <https://www.gnu.org/licenses/>. | # along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||||
| import bpy | import bpy | ||||||
| from .classes.draw import draw_file_new_templates, draw_file_default_operators, draw_ws_menu_add | from bpy.types import TOPBAR_MT_file_new, TOPBAR_MT_file_defaults, TOPBAR_MT_workspace_menu | ||||||
| from .classes.splash import WM_MT_splash, CT_MT_splash_mode, CT_OT_splash_custom, CT_OT_splash_default | from .src.funcs import draw_file_new_templates, draw_file_default_operators, draw_ws_menu_add | ||||||
| from .classes.ots import CustomTemplatesPreferences, TemplateItem, CT_OT_export_templates, CT_OT_import_templates, CT_MT_templates_menu, CT_OT_select_template_popup, CT_OT_add_template_popup, CT_OT_add, CT_OT_remove, CT_OT_move_down, CT_OT_move_up, CT_OT_open_preferences, CT_OT_add_templates_from_folder, CT_OT_clear, CT_MT_workspace_add, CT_OT_template_workspaces, CT_OT_add_workspace | from .src.splash import WM_MT_splash | ||||||
|  | from .src.menus import CT_MT_splash_mode, CT_MT_templates_menu, CT_MT_workspace_add, CT_MT_more_templates | ||||||
|  | from .src.ops import CT_OT_export_templates, CT_OT_start_from, CT_OT_import_templates, CT_OT_select_template_popup, CT_OT_add_template_popup, CT_OT_add, CT_OT_remove, CT_OT_move_down, CT_OT_move_up, CT_OT_open_preferences, CT_OT_add_templates_from_folder, CT_OT_clear, CT_OT_template_workspaces, CT_OT_add_workspace | ||||||
|  | from .src.prefs import CustomTemplatesPreferences, TemplateItem | ||||||
| 
 | 
 | ||||||
| bl_info = { | bl_info = { | ||||||
|     "id": "custom_templates", |     "id": "custom_templates", | ||||||
| @ -30,17 +33,16 @@ bl_info = { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| classes = [WM_MT_splash, | classes = [WM_MT_splash, | ||||||
|            CT_OT_add_workspace, |  | ||||||
|            CT_OT_template_workspaces, |  | ||||||
|            CT_MT_workspace_add, |            CT_MT_workspace_add, | ||||||
|            TemplateItem, |            CT_MT_splash_mode, | ||||||
|  |            CT_MT_templates_menu, | ||||||
|  |            CT_MT_more_templates, | ||||||
|  |            CT_OT_add_workspace, | ||||||
|  |            CT_OT_start_from, | ||||||
|  |            CT_OT_template_workspaces, | ||||||
|            CT_OT_export_templates, |            CT_OT_export_templates, | ||||||
|            CT_OT_import_templates, |            CT_OT_import_templates, | ||||||
|            CT_OT_splash_custom,  |  | ||||||
|            CT_OT_splash_default, |  | ||||||
|            CT_OT_open_preferences, |            CT_OT_open_preferences, | ||||||
|            CT_MT_splash_mode,  |  | ||||||
|            CT_MT_templates_menu, |  | ||||||
|            CT_OT_move_up, |            CT_OT_move_up, | ||||||
|            CT_OT_move_down, |            CT_OT_move_down, | ||||||
|            CT_OT_add, |            CT_OT_add, | ||||||
| @ -49,26 +51,27 @@ classes = [WM_MT_splash, | |||||||
|            CT_OT_add_template_popup, |            CT_OT_add_template_popup, | ||||||
|            CT_OT_select_template_popup, |            CT_OT_select_template_popup, | ||||||
|            CT_OT_add_templates_from_folder, |            CT_OT_add_templates_from_folder, | ||||||
|  |            TemplateItem, | ||||||
|            CustomTemplatesPreferences] |            CustomTemplatesPreferences] | ||||||
| 
 | 
 | ||||||
| og_splash = None; | og_splash = None | ||||||
| 
 | 
 | ||||||
| def register(): | def register(): | ||||||
|     global og_splash |     global og_splash | ||||||
|     og_splash = bpy.types.WM_MT_splash; |     og_splash = bpy.types.WM_MT_splash | ||||||
|     for c in classes: |     for c in classes: | ||||||
|         bpy.utils.register_class(c) |         bpy.utils.register_class(c) | ||||||
|     bpy.types.TOPBAR_MT_file_new.append(draw_file_new_templates) |     TOPBAR_MT_file_new.append(draw_file_new_templates) | ||||||
|     bpy.types.TOPBAR_MT_file_defaults.append(draw_file_default_operators) |     TOPBAR_MT_file_defaults.append(draw_file_default_operators) | ||||||
|     bpy.types.TOPBAR_MT_workspace_menu.append(draw_ws_menu_add) |     TOPBAR_MT_workspace_menu.append(draw_ws_menu_add) | ||||||
| 
 | 
 | ||||||
| def unregister(): | def unregister(): | ||||||
|     for c in reversed(classes): |     for c in reversed(classes): | ||||||
|         bpy.utils.unregister_class(c) |         bpy.utils.unregister_class(c) | ||||||
|     bpy.utils.register_class(og_splash) |     bpy.utils.register_class(og_splash) | ||||||
|     bpy.types.TOPBAR_MT_file_new.remove(draw_file_new_templates) |     TOPBAR_MT_file_new.remove(draw_file_new_templates) | ||||||
|     bpy.types.TOPBAR_MT_file_defaults.remove(draw_file_default_operators) |     TOPBAR_MT_file_defaults.remove(draw_file_default_operators) | ||||||
|     bpy.types.TOPBAR_MT_workspace_menu.remove(draw_ws_menu_add) |     TOPBAR_MT_workspace_menu.remove(draw_ws_menu_add) | ||||||
|      | 
 | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|     registers() |     registers() | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| schema_version = "1.0.0" | schema_version = "1.0.0" | ||||||
| id = "custom_templates" | id = "custom_templates" | ||||||
| version = "1.4.0" | version = "1.5.0" | ||||||
| name = "Custom Templates" | name = "Custom Templates" | ||||||
| tagline = "Use your own .blend files as template options for new projects" | tagline = "Use your own .blend files as template options for new projects" | ||||||
| maintainer = "Francesco Bellini <doc.open.dev@gmail.com>" | maintainer = "Francesco Bellini <doc.open.dev@gmail.com>" | ||||||
| @ -11,4 +11,4 @@ blender_version_min = "4.2.0" | |||||||
| license = ["SPDX:GPL-3.0-or-later"] | license = ["SPDX:GPL-3.0-or-later"] | ||||||
| copyright = ["2024 Francesco Bellini"] | copyright = ["2024 Francesco Bellini"] | ||||||
| [permissions] | [permissions] | ||||||
| files = "JSON Import/Export of templates list / Add from folder feature" | files = "JSON Import/Export of templates list / Add templates from folder" | ||||||
|  | |||||||
| @ -1,21 +1,54 @@ | |||||||
| from .. import __package__ as base_package | import os | ||||||
| from .ots import name_from_path |  | ||||||
| import bpy | import bpy | ||||||
|  | from .. import __package__ as base_package | ||||||
|  | 
 | ||||||
|  | def already_present(self, prefs, path, report=True): | ||||||
|  |     for p in prefs.projects: | ||||||
|  |         if p.path == path: | ||||||
|  |             if report: | ||||||
|  |                 self.report( | ||||||
|  |                     {'WARNING'}, f'Current file is already in the templates list as "{p.name}".') | ||||||
|  |             return True | ||||||
|  |     return False | ||||||
|  | 
 | ||||||
|  | def name_from_path(path): | ||||||
|  |     if path: | ||||||
|  |         return os.path.splitext(os.path.basename(path))[0] | ||||||
|  |     else: | ||||||
|  |         return "" | ||||||
|  | 
 | ||||||
|  | def on_path_update(self, context): | ||||||
|  |     if not self.name and self.path: | ||||||
|  |         self.name = name_from_path(self.path) | ||||||
|  |         context.preferences.is_dirty = True | ||||||
|  |     if self.path and self.path.startswith('//'): | ||||||
|  |         self.path = bpy.path.abspath(self.path) | ||||||
|  |         context.preferences.is_dirty = True | ||||||
|  | 
 | ||||||
|  | def pref(): | ||||||
|  |     return bpy.context.preferences.addons[base_package].preferences | ||||||
|  |          | ||||||
|  | def has_templates(): | ||||||
|  |     return len(pref().projects) > 0 | ||||||
| 
 | 
 | ||||||
| def draw_file_new_templates(self, context): | def draw_file_new_templates(self, context): | ||||||
|     layout = self.layout |     layout = self.layout | ||||||
|     prefs = context.preferences.addons[base_package].preferences |     if has_templates(): | ||||||
|     if len(prefs.projects) > 0: |  | ||||||
|         layout.separator() |         layout.separator() | ||||||
|     draw_templates(layout, context) |     draw_templates(layout, context) | ||||||
| 
 | 
 | ||||||
| def draw_templates(layout, context, splash_mode=False): | def draw_templates(layout, context, splash_mode=False): | ||||||
|     prefs = context.preferences.addons[base_package].preferences |     prefs = pref() | ||||||
|     for i in range(min(5, len(prefs.projects)) if splash_mode else len(prefs.projects)): |     for i in range(min(5 if len(prefs.projects) <= 5 else 4, len(prefs.projects)) if splash_mode else len(prefs.projects)): | ||||||
|         project = prefs.projects[i] |         project = prefs.projects[i] | ||||||
|         if project.path: |         if project.path: | ||||||
|             layout.operator( |             layout.operator( | ||||||
|                 "wm.read_homefile", text=(project.name if project.name else name_from_path(project.path)), icon="FILE_NEW" if splash_mode else "NONE").filepath = project.path |                 "wm.read_homefile", text=(project.name if project.name else name_from_path(project.path)), icon="FILE_NEW" if splash_mode else "NONE").filepath = project.path | ||||||
|  |     if splash_mode and len(prefs.projects) > 5: | ||||||
|  |         layout.menu("CT_MT_more_templates", text="More...", icon="FILE_NEW") | ||||||
|  |     if not splash_mode and prefs.start_from and context.area.type == 'TOPBAR': | ||||||
|  |         layout.separator() | ||||||
|  |         layout.operator("ct.start_from", icon="FILE_FOLDER") | ||||||
| 
 | 
 | ||||||
| def draw_file_default_operators(self, context): | def draw_file_default_operators(self, context): | ||||||
|     layout = self.layout |     layout = self.layout | ||||||
| @ -39,8 +72,6 @@ def draw_file_default_operators(self, context): | |||||||
| 
 | 
 | ||||||
| def draw_ws_menu_add(self, context): | def draw_ws_menu_add(self, context): | ||||||
|     layout = self.layout |     layout = self.layout | ||||||
|     prefs = context.preferences.addons[base_package].preferences |     if has_templates(): | ||||||
|     if prefs.projects: |  | ||||||
|         layout.separator() |         layout.separator() | ||||||
|         layout.menu("CT_MT_workspace_add", text="Add from Custom Templates", icon="WORKSPACE") |         layout.menu("CT_MT_workspace_add", text="Add from Custom Templates", icon="WORKSPACE") | ||||||
|      |  | ||||||
							
								
								
									
										60
									
								
								addon/src/menus.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								addon/src/menus.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,60 @@ | |||||||
|  | from .. import __package__ as base_package | ||||||
|  | import os | ||||||
|  | import bpy | ||||||
|  | import json | ||||||
|  | from bpy.types import Menu | ||||||
|  | from .funcs import pref, name_from_path | ||||||
|  | 
 | ||||||
|  | class CT_MT_templates_menu(Menu): | ||||||
|  |     bl_label = "Manage your custom templates" | ||||||
|  |     bl_description = "Import, export, add from folder (with controllable recursion depth), clear current templates" | ||||||
|  | 
 | ||||||
|  |     def draw(self, context): | ||||||
|  |         layout = self.layout | ||||||
|  |         layout.operator("ct.add_templates_from_folder", | ||||||
|  |                         text="Add from folder", icon="ADD") | ||||||
|  |         layout.operator( | ||||||
|  |             "ct.clear", text="Clear current templates", icon="TRASH") | ||||||
|  |         layout.separator() | ||||||
|  |         layout.operator("ct.import_templates", | ||||||
|  |                         text="Import templates", icon="IMPORT") | ||||||
|  |         layout.operator("ct.export_templates", | ||||||
|  |                         text="Export templates", icon="EXPORT") | ||||||
|  | 
 | ||||||
|  | class CT_MT_splash_mode(Menu): | ||||||
|  |     bl_idname = "CT_MT_splash_mode" | ||||||
|  |     bl_label = "Custom Templates Switch" | ||||||
|  |     bl_description = "Swtich between Blender's default and your Custom Templates" | ||||||
|  | 
 | ||||||
|  |     def draw(self, context): | ||||||
|  |         layout = self.layout | ||||||
|  |         prefs = pref() | ||||||
|  |              | ||||||
|  |         layout.operator("ct.open_preferences", text="Manage templates") | ||||||
|  |         layout.separator() | ||||||
|  |         layout.prop(prefs, "override_splash") | ||||||
|  | 
 | ||||||
|  | class CT_MT_more_templates(Menu): | ||||||
|  |     bl_idname = "CT_MT_more_templates" | ||||||
|  |     bl_label = "Other Custom Templates" | ||||||
|  |     bl_description = "Show the other templates in the splash screen" | ||||||
|  | 
 | ||||||
|  |     def draw(self, context): | ||||||
|  |         layout = self.layout | ||||||
|  |         prefs = pref() | ||||||
|  |          | ||||||
|  |         for t in prefs.projects[4:]: | ||||||
|  |             layout.operator( | ||||||
|  |                 "wm.read_homefile", text=(t.name if t.name else name_from_path(t.path)), icon="FILE_NEW").filepath = t.path | ||||||
|  | 
 | ||||||
|  | class CT_MT_workspace_add(Menu): | ||||||
|  |     bl_label = "Add workspace from Custom Templates" | ||||||
|  | 
 | ||||||
|  |     def draw(self, context): | ||||||
|  |         layout = self.layout | ||||||
|  |         templates = pref().projects | ||||||
|  |         layout.label(text="Add workspace from Custom Templates", icon="ADD") | ||||||
|  |         layout.separator() | ||||||
|  |         for i in range(len(templates)): | ||||||
|  |             t = templates[i] | ||||||
|  |             layout.operator("CT_OT_template_workspaces", text=t.name).index = i | ||||||
| @ -1,103 +1,10 @@ | |||||||
| from .. import __package__ as base_package |  | ||||||
| import os | import os | ||||||
| import bpy | import bpy | ||||||
| import json | import json | ||||||
| from bpy.types import Operator, Menu, PropertyGroup, AddonPreferences | from bpy.types import Operator | ||||||
| from bpy.props import StringProperty, CollectionProperty, IntProperty, BoolProperty | from bpy.props import StringProperty, IntProperty, BoolProperty | ||||||
| 
 | from .funcs import pref, has_templates, name_from_path, already_present, on_path_update | ||||||
| def already_present(self, prefs, path, report=True): | from .. import __package__ as base_package | ||||||
|     for p in prefs.projects: |  | ||||||
|         if p.path == path: |  | ||||||
|             if report: |  | ||||||
|                 self.report( |  | ||||||
|                     {'WARNING'}, f'Current file is already in the templates list as "{p.name}".') |  | ||||||
|             return True |  | ||||||
|     return False |  | ||||||
| 
 |  | ||||||
| def name_from_path(path): |  | ||||||
|     if path: |  | ||||||
|         return os.path.splitext(os.path.basename(path))[0] |  | ||||||
|     else: |  | ||||||
|         return "" |  | ||||||
| 
 |  | ||||||
| def on_path_update(self, context): |  | ||||||
|     if not self.name and self.path: |  | ||||||
|         self.name = name_from_path(self.path) |  | ||||||
|         context.preferences.is_dirty = True |  | ||||||
|     if self.path and self.path.startswith('//'): |  | ||||||
|         self.path = bpy.path.abspath(self.path) |  | ||||||
|         context.preferences.is_dirty = True |  | ||||||
| 
 |  | ||||||
| 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', update=on_path_update) |  | ||||||
| 
 |  | ||||||
| class CustomTemplatesPreferences(AddonPreferences): |  | ||||||
|     bl_idname = base_package |  | ||||||
| 
 |  | ||||||
|     override_splash: BoolProperty( |  | ||||||
|         default=True, name="Use Custom Templates", description="Override Splash Screen's 'New File' list with your Custom Templates") |  | ||||||
|     projects: CollectionProperty(type=TemplateItem) |  | ||||||
|     active_template_index: IntProperty( |  | ||||||
|         description="Index of the selected template") |  | ||||||
| 
 |  | ||||||
|     def draw(self, context): |  | ||||||
|         layout = self.layout |  | ||||||
| 
 |  | ||||||
|         layout.label( |  | ||||||
|             text=f"Your custom templates ({len(self.projects)})") |  | ||||||
|         row = layout.row() |  | ||||||
|         row.template_list("UI_UL_list", "custom_templates", |  | ||||||
|                           self, "projects", self, "active_template_index", rows=6, maxrows=6) |  | ||||||
| 
 |  | ||||||
|         col = row.column(align=True) |  | ||||||
|         col.menu("CT_MT_templates_menu", icon='DOWNARROW_HLT', text="") |  | ||||||
|         col.separator() |  | ||||||
|         col.operator("ct.add_templates_from_folder", |  | ||||||
|                      text="", icon="FILE_FOLDER") |  | ||||||
|         col.operator("ct.add", icon='ADD', text="") |  | ||||||
|         col.operator("ct.remove", icon='REMOVE', text="") |  | ||||||
|         col.separator() |  | ||||||
|         col.operator("ct.move_up", icon='TRIA_UP', text="") |  | ||||||
|         col.operator("ct.move_down", icon='TRIA_DOWN', text="") |  | ||||||
| 
 |  | ||||||
|         if self.projects: |  | ||||||
|             project = self.projects[self.active_template_index] |  | ||||||
|             layout.prop(project, "path") |  | ||||||
|             layout.prop(project, "name") |  | ||||||
|              |  | ||||||
|         b = layout.box() |  | ||||||
|         b.label(text="Splash Screen", icon="SETTINGS") |  | ||||||
|         b.prop(self, "override_splash") |  | ||||||
|         box = b.box() |  | ||||||
|         if self.override_splash and self.projects: |  | ||||||
|             box.label(text="Custom templates will be shown in the Splash Screen.") |  | ||||||
|              |  | ||||||
|         if not self.override_splash or len(self.projects) == 0: |  | ||||||
|             box.label( |  | ||||||
|                 text="The default Blender list will be shown in the Splash Screen.", icon=("ERROR" if not self.projects else "NONE")) |  | ||||||
| 
 |  | ||||||
|         if len(self.projects) > 5: |  | ||||||
|             box.label( |  | ||||||
|                 text="Note: Only the first 5 templates in the list will be shown in the splash screen.") |  | ||||||
|          |  | ||||||
| class CT_MT_templates_menu(Menu): |  | ||||||
|     bl_label = "Manage your custom templates" |  | ||||||
|     bl_description = "Import, export, add from folder (with controllable recursion depth), clear current templates" |  | ||||||
| 
 |  | ||||||
|     def draw(self, context): |  | ||||||
|         layout = self.layout |  | ||||||
|         layout.operator("ct.add_templates_from_folder", |  | ||||||
|                         text="Add from folder", icon="ADD") |  | ||||||
|         layout.operator( |  | ||||||
|             "ct.clear", text="Clear current templates", icon="TRASH") |  | ||||||
|         layout.separator() |  | ||||||
|         layout.operator("ct.import_templates", |  | ||||||
|                         text="Import templates", icon="IMPORT") |  | ||||||
|         layout.operator("ct.export_templates", |  | ||||||
|                         text="Export templates", icon="EXPORT") |  | ||||||
| 
 | 
 | ||||||
| class CT_OT_export_templates(Operator): | class CT_OT_export_templates(Operator): | ||||||
|     bl_idname = "ct.export_templates" |     bl_idname = "ct.export_templates" | ||||||
| @ -108,7 +15,7 @@ class CT_OT_export_templates(Operator): | |||||||
|         subtype="FILE_PATH", description="Select the path for the exported file", default="templates.json") |         subtype="FILE_PATH", description="Select the path for the exported file", default="templates.json") | ||||||
| 
 | 
 | ||||||
|     def execute(self, context): |     def execute(self, context): | ||||||
|         prefs = context.preferences.addons[base_package].preferences |         prefs = pref() | ||||||
|         with open(self.filepath, 'w') as f: |         with open(self.filepath, 'w') as f: | ||||||
|             projects = [{"name": project.name, "path": project.path} |             projects = [{"name": project.name, "path": project.path} | ||||||
|                         for project in prefs.projects] |                         for project in prefs.projects] | ||||||
| @ -123,7 +30,7 @@ class CT_OT_export_templates(Operator): | |||||||
| 
 | 
 | ||||||
|     @classmethod |     @classmethod | ||||||
|     def poll(cls, context): |     def poll(cls, context): | ||||||
|         return len(context.preferences.addons[base_package].preferences.projects) > 0 |         return has_templates() | ||||||
| 
 | 
 | ||||||
| class CT_OT_import_templates(Operator): | class CT_OT_import_templates(Operator): | ||||||
|     bl_idname = "ct.import_templates" |     bl_idname = "ct.import_templates" | ||||||
| @ -134,7 +41,7 @@ class CT_OT_import_templates(Operator): | |||||||
|         subtype="FILE_PATH", description="Select the .json file to load") |         subtype="FILE_PATH", description="Select the .json file to load") | ||||||
| 
 | 
 | ||||||
|     def execute(self, context): |     def execute(self, context): | ||||||
|         prefs = context.preferences.addons[base_package].preferences |         prefs = pref() | ||||||
| 
 | 
 | ||||||
|         if os.path.exists(self.filepath): |         if os.path.exists(self.filepath): | ||||||
|             with open(self.filepath, 'r') as f: |             with open(self.filepath, 'r') as f: | ||||||
| @ -164,7 +71,7 @@ class CT_OT_add(Operator): | |||||||
|     bl_description = "Add new template" |     bl_description = "Add new template" | ||||||
| 
 | 
 | ||||||
|     def execute(self, context): |     def execute(self, context): | ||||||
|         prefs = context.preferences.addons[base_package].preferences |         prefs = pref() | ||||||
|         prefs.projects.add() |         prefs.projects.add() | ||||||
|         prefs.active_template_index = len(prefs.projects) - 1 |         prefs.active_template_index = len(prefs.projects) - 1 | ||||||
|         context.preferences.is_dirty = True |         context.preferences.is_dirty = True | ||||||
| @ -176,7 +83,7 @@ class CT_OT_remove(Operator): | |||||||
|     bl_description = "Remove selected template" |     bl_description = "Remove selected template" | ||||||
| 
 | 
 | ||||||
|     def execute(self, context): |     def execute(self, context): | ||||||
|         prefs = context.preferences.addons[base_package].preferences |         prefs = pref() | ||||||
|         prefs.projects.remove(prefs.active_template_index) |         prefs.projects.remove(prefs.active_template_index) | ||||||
|         prefs.active_template_index = min( |         prefs.active_template_index = min( | ||||||
|             max(0, prefs.active_template_index - 1), len(prefs.projects) - 1) |             max(0, prefs.active_template_index - 1), len(prefs.projects) - 1) | ||||||
| @ -185,7 +92,7 @@ class CT_OT_remove(Operator): | |||||||
| 
 | 
 | ||||||
|     @classmethod |     @classmethod | ||||||
|     def poll(cls, context): |     def poll(cls, context): | ||||||
|         return len(context.preferences.addons[base_package].preferences.projects) > 0 |         return has_templates() | ||||||
| 
 | 
 | ||||||
| class CT_OT_move_up(Operator): | class CT_OT_move_up(Operator): | ||||||
|     bl_idname = "ct.move_up" |     bl_idname = "ct.move_up" | ||||||
| @ -193,7 +100,7 @@ class CT_OT_move_up(Operator): | |||||||
|     bl_description = "Move the selected template up in the list" |     bl_description = "Move the selected template up in the list" | ||||||
| 
 | 
 | ||||||
|     def execute(self, context): |     def execute(self, context): | ||||||
|         prefs = context.preferences.addons[base_package].preferences |         prefs = pref() | ||||||
|         index = prefs.active_template_index |         index = prefs.active_template_index | ||||||
| 
 | 
 | ||||||
|         if index > 0: |         if index > 0: | ||||||
| @ -206,7 +113,7 @@ class CT_OT_move_up(Operator): | |||||||
| 
 | 
 | ||||||
|     @classmethod |     @classmethod | ||||||
|     def poll(cls, context): |     def poll(cls, context): | ||||||
|         return len(context.preferences.addons[base_package].preferences.projects) > 0 |         return has_templates() | ||||||
| 
 | 
 | ||||||
| class CT_OT_move_down(Operator): | class CT_OT_move_down(Operator): | ||||||
|     bl_idname = "ct.move_down" |     bl_idname = "ct.move_down" | ||||||
| @ -214,7 +121,7 @@ class CT_OT_move_down(Operator): | |||||||
|     bl_description = "Move the selected template down in the list" |     bl_description = "Move the selected template down in the list" | ||||||
| 
 | 
 | ||||||
|     def execute(self, context): |     def execute(self, context): | ||||||
|         prefs = context.preferences.addons[base_package].preferences |         prefs = pref() | ||||||
|         index = prefs.active_template_index |         index = prefs.active_template_index | ||||||
| 
 | 
 | ||||||
|         if index < len(prefs.projects) - 1: |         if index < len(prefs.projects) - 1: | ||||||
| @ -227,7 +134,7 @@ class CT_OT_move_down(Operator): | |||||||
| 
 | 
 | ||||||
|     @classmethod |     @classmethod | ||||||
|     def poll(cls, context): |     def poll(cls, context): | ||||||
|         return len(context.preferences.addons[base_package].preferences.projects) > 0 |         return has_templates() | ||||||
| 
 | 
 | ||||||
| class CT_OT_clear(Operator): | class CT_OT_clear(Operator): | ||||||
|     bl_idname = "ct.clear" |     bl_idname = "ct.clear" | ||||||
| @ -235,7 +142,7 @@ class CT_OT_clear(Operator): | |||||||
|     bl_description = "Clear the current list of templates (remove all templates)" |     bl_description = "Clear the current list of templates (remove all templates)" | ||||||
| 
 | 
 | ||||||
|     def execute(self, context): |     def execute(self, context): | ||||||
|         prefs = context.preferences.addons[base_package].preferences |         prefs = pref() | ||||||
|         prefs.projects.clear() |         prefs.projects.clear() | ||||||
|         prefs.active_template_index = 0 |         prefs.active_template_index = 0 | ||||||
|         context.preferences.is_dirty = True |         context.preferences.is_dirty = True | ||||||
| @ -246,7 +153,7 @@ class CT_OT_clear(Operator): | |||||||
| 
 | 
 | ||||||
|     @classmethod |     @classmethod | ||||||
|     def poll(cls, context): |     def poll(cls, context): | ||||||
|         return len(context.preferences.addons[base_package].preferences.projects) > 0 |         return has_templates() | ||||||
| 
 | 
 | ||||||
| class CT_OT_add_template_popup(Operator): | class CT_OT_add_template_popup(Operator): | ||||||
|     bl_idname = "ct.add_template_popup" |     bl_idname = "ct.add_template_popup" | ||||||
| @ -256,7 +163,7 @@ class CT_OT_add_template_popup(Operator): | |||||||
|     name: StringProperty(name="Project Name") |     name: StringProperty(name="Project Name") | ||||||
| 
 | 
 | ||||||
|     def execute(self, context): |     def execute(self, context): | ||||||
|         prefs = context.preferences.addons[base_package].preferences |         prefs = pref() | ||||||
|         if not already_present(self, prefs, bpy.data.filepath): |         if not already_present(self, prefs, bpy.data.filepath): | ||||||
|             if bpy.data.filepath: |             if bpy.data.filepath: | ||||||
|                 new_project = prefs.projects.add() |                 new_project = prefs.projects.add() | ||||||
| @ -283,7 +190,7 @@ class CT_OT_select_template_popup(Operator): | |||||||
|     name: StringProperty(name="Template Name") |     name: StringProperty(name="Template Name") | ||||||
| 
 | 
 | ||||||
|     def execute(self, context): |     def execute(self, context): | ||||||
|         prefs = context.preferences.addons[base_package].preferences |         prefs = pref() | ||||||
|         if not already_present(self, prefs, self.path): |         if not already_present(self, prefs, self.path): | ||||||
|             template = prefs.projects.add() |             template = prefs.projects.add() | ||||||
|             template.name = self.name |             template.name = self.name | ||||||
| @ -297,7 +204,7 @@ class CT_OT_select_template_popup(Operator): | |||||||
|     def invoke(self, context, event): |     def invoke(self, context, event): | ||||||
|         return context.window_manager.invoke_props_dialog(self) |         return context.window_manager.invoke_props_dialog(self) | ||||||
| 
 | 
 | ||||||
| class CT_OT_add_templates_from_folder(bpy.types.Operator): | class CT_OT_add_templates_from_folder(Operator): | ||||||
|     bl_idname = "ct.add_templates_from_folder" |     bl_idname = "ct.add_templates_from_folder" | ||||||
|     bl_label = "Add Templates from Folder" |     bl_label = "Add Templates from Folder" | ||||||
|     bl_description = "Add templates from a specified folder containing .blend files" |     bl_description = "Add templates from a specified folder containing .blend files" | ||||||
| @ -308,7 +215,7 @@ class CT_OT_add_templates_from_folder(bpy.types.Operator): | |||||||
|         name="Depth", description="Depth of recursion (default 1)", default=1, min=1) |         name="Depth", description="Depth of recursion (default 1)", default=1, min=1) | ||||||
| 
 | 
 | ||||||
|     def execute(self, context): |     def execute(self, context): | ||||||
|         prefs = context.preferences.addons[base_package].preferences |         prefs = pref() | ||||||
| 
 | 
 | ||||||
|         if os.path.exists(self.directory): |         if os.path.exists(self.directory): | ||||||
|             blend_files = [] |             blend_files = [] | ||||||
| @ -376,7 +283,7 @@ class CT_OT_template_workspaces(Operator): | |||||||
| 
 | 
 | ||||||
|     def draw_ws(self, s, context): |     def draw_ws(self, s, context): | ||||||
|         layout = s.layout |         layout = s.layout | ||||||
|         template = context.preferences.addons[base_package].preferences.projects[self.index] |         template = pref().projects[self.index] | ||||||
|         with bpy.data.libraries.load(template.path) as (data, _): |         with bpy.data.libraries.load(template.path) as (data, _): | ||||||
|             for w in data.workspaces: |             for w in data.workspaces: | ||||||
|                 op = layout.operator("ct.add_workspace", text=w) |                 op = layout.operator("ct.add_workspace", text=w) | ||||||
| @ -388,18 +295,34 @@ class CT_OT_template_workspaces(Operator): | |||||||
| 
 | 
 | ||||||
|     def invoke(self, context, event): |     def invoke(self, context, event): | ||||||
|         wm = context.window_manager |         wm = context.window_manager | ||||||
|         template = context.preferences.addons[base_package].preferences.projects[self.index] |         template = pref().projects[self.index] | ||||||
|         wm.popup_menu(self.draw_ws, title=f"Workspaces from '{template.name}'", icon="ADD") |         wm.popup_menu(self.draw_ws, title=f"Workspaces from '{template.name}'", icon="ADD") | ||||||
|         return {'RUNNING_MODAL'} |         return {'RUNNING_MODAL'} | ||||||
| 
 | 
 | ||||||
| class CT_MT_workspace_add(Menu): | class CT_OT_start_from(Operator): | ||||||
|     bl_label = "Add workspace from Custom Templates" |     bl_idname = "ct.start_from" | ||||||
|  |     bl_label = "Start from..." | ||||||
|  |     bl_description = "Use any selected .blend file as template (and optionally add it to the list)" | ||||||
| 
 | 
 | ||||||
|     def draw(self, context): |     filepath: StringProperty( | ||||||
|         layout = self.layout |         subtype="FILE_PATH", description="Select the .json file to load", update=on_path_update) | ||||||
|         templates = context.preferences.addons[base_package].preferences.projects |     add: BoolProperty(default=False, name="Add template", description="Add this file in your templates") | ||||||
|         layout.label(text="Add workspace from Custom Templates", icon="ADD") |     name: StringProperty(name="Name", description="The name for this template (if empty, file name will be used)") | ||||||
|         layout.separator() | 
 | ||||||
|         for i in range(len(templates)): |     def execute(self, context): | ||||||
|             t = templates[i] |         prefs = pref() | ||||||
|             layout.operator("CT_OT_template_workspaces", text=t.name).index = i |         if self.filepath: | ||||||
|  |             if self.add: | ||||||
|  |                 template = prefs.projects.add() | ||||||
|  |                 template.name = self.name if self.name else name_from_path(self.filepath) | ||||||
|  |                 template.path = self.filepath | ||||||
|  |                 context.preferences.is_dirty = True | ||||||
|  |                  | ||||||
|  |             bpy.ops.wm.read_homefile(filepath=self.filepath) | ||||||
|  |         else: | ||||||
|  |             self.report({'WARNING'}, "This option can only be used from File > New menu") | ||||||
|  |         return {'FINISHED'} | ||||||
|  | 
 | ||||||
|  |     def invoke(self, context, event): | ||||||
|  |         context.window_manager.fileselect_add(self) | ||||||
|  |         return {'RUNNING_MODAL'} | ||||||
							
								
								
									
										60
									
								
								addon/src/prefs.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								addon/src/prefs.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,60 @@ | |||||||
|  | import bpy | ||||||
|  | from bpy.types import PropertyGroup, AddonPreferences | ||||||
|  | from bpy.props import StringProperty, CollectionProperty, IntProperty, BoolProperty | ||||||
|  | from .funcs import on_path_update | ||||||
|  | from .. import __package__ as base_package | ||||||
|  | 
 | ||||||
|  | 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', update=on_path_update) | ||||||
|  | 
 | ||||||
|  | class CustomTemplatesPreferences(AddonPreferences): | ||||||
|  |     bl_idname = base_package | ||||||
|  | 
 | ||||||
|  |     override_splash: BoolProperty( | ||||||
|  |         default=True, name="Use Custom Templates", description="Override Splash Screen's 'New File' list with your Custom Templates") | ||||||
|  |     start_from: BoolProperty( | ||||||
|  |         default=False, name="Add option: File > New > Start from...", description="Show an option in File > New menu to start a new project from any .blend file") | ||||||
|  |     projects: CollectionProperty(type=TemplateItem) | ||||||
|  |     active_template_index: IntProperty( | ||||||
|  |         description="Index of the selected template") | ||||||
|  | 
 | ||||||
|  |     def draw(self, context): | ||||||
|  |         layout = self.layout | ||||||
|  | 
 | ||||||
|  |         layout.label( | ||||||
|  |             text=f"Your custom templates ({len(self.projects)})") | ||||||
|  |         row = layout.row() | ||||||
|  |         row.template_list("UI_UL_list", "custom_templates", | ||||||
|  |                           self, "projects", self, "active_template_index", rows=6, maxrows=6) | ||||||
|  |         col = row.column(align=True) | ||||||
|  |         col.menu("CT_MT_templates_menu", icon='DOWNARROW_HLT', text="") | ||||||
|  |         col.separator() | ||||||
|  |         col.operator("ct.add_templates_from_folder", text="", icon="FILE_FOLDER") | ||||||
|  |         col.operator("ct.add", icon='ADD', text="") | ||||||
|  |         col.operator("ct.remove", icon='REMOVE', text="") | ||||||
|  |         col.separator() | ||||||
|  |         col.operator("ct.move_up", icon='TRIA_UP', text="") | ||||||
|  |         col.operator("ct.move_down", icon='TRIA_DOWN', text="") | ||||||
|  | 
 | ||||||
|  |         if self.projects: | ||||||
|  |             project = self.projects[self.active_template_index] | ||||||
|  |             layout.prop(project, "path") | ||||||
|  |             layout.prop(project, "name") | ||||||
|  | 
 | ||||||
|  |         b = layout.box() | ||||||
|  |         b.label(text="Splash Screen", icon="SETTINGS") | ||||||
|  |         b.prop(self, "override_splash") | ||||||
|  |         box = b.box() | ||||||
|  |         if self.override_splash and self.projects: | ||||||
|  |             box.label(text="Custom templates will be shown in the Splash Screen.") | ||||||
|  |    | ||||||
|  |         if not self.override_splash or len(self.projects) == 0: | ||||||
|  |             box.label( | ||||||
|  |                 text="The default Blender list will be shown in the Splash Screen.", icon=("ERROR" if not self.projects else "NONE")) | ||||||
|  | 
 | ||||||
|  |         b = layout.box() | ||||||
|  |         b.label(text="Extra", icon="SETTINGS") | ||||||
|  |         b.prop(self, "start_from") | ||||||
| @ -1,8 +1,8 @@ | |||||||
| # Ref WM_MT_splash https://projects.blender.org/blender/blender/src/commit/5a9fe638dedb179050c4929ea8fcdec80d221af2/scripts/startup/bl_operators/wm.py#L3296 | # Ref WM_MT_splash https://projects.blender.org/blender/blender/src/commit/5a9fe638dedb179050c4929ea8fcdec80d221af2/scripts/startup/bl_operators/wm.py#L3296 | ||||||
| # Ref TOPBAR_MT_file_new https://projects.blender.org/blender/blender/src/commit/5a9fe638dedb179050c4929ea8fcdec80d221af2/scripts/startup/bl_ui/space_topbar.py#L286 | # Ref TOPBAR_MT_file_new https://projects.blender.org/blender/blender/src/commit/5a9fe638dedb179050c4929ea8fcdec80d221af2/scripts/startup/bl_ui/space_topbar.py#L286 | ||||||
| from .. import __package__ as base_package |  | ||||||
| from .draw import draw_templates |  | ||||||
| import bpy | import bpy | ||||||
|  | from .funcs import draw_templates, pref | ||||||
|  | from .. import __package__ as base_package | ||||||
| # Clone of the WM_MT_splash (the section under the image of the Splash) | # Clone of the WM_MT_splash (the section under the image of the Splash) | ||||||
| # It's edited (only in #Templates part) to add a menu in the New File section  | # It's edited (only in #Templates part) to add a menu in the New File section  | ||||||
| # to switch between blender's original templates and Custom Templates. | # to switch between blender's original templates and Custom Templates. | ||||||
| @ -12,7 +12,7 @@ class WM_MT_splash(bpy.types.Menu): | |||||||
|      |      | ||||||
|     def draw(self, context): |     def draw(self, context): | ||||||
|         layout = self.layout |         layout = self.layout | ||||||
|         prefs = context.preferences.addons[base_package].preferences |         prefs = pref() | ||||||
|         layout.operator_context = 'EXEC_DEFAULT' |         layout.operator_context = 'EXEC_DEFAULT' | ||||||
|         layout.emboss = 'PULLDOWN_MENU' |         layout.emboss = 'PULLDOWN_MENU' | ||||||
|         split = layout.split() |         split = layout.split() | ||||||
| @ -26,8 +26,8 @@ class WM_MT_splash(bpy.types.Menu): | |||||||
|             col1.operator_context = 'INVOKE_DEFAULT' |             col1.operator_context = 'INVOKE_DEFAULT' | ||||||
|             draw_templates(col1, context, True) |             draw_templates(col1, context, True) | ||||||
|         else: |         else: | ||||||
|             colA.label(text="New File") |  | ||||||
|             # Call original code |             # Call original code | ||||||
|  |             colA.label(text="New File") | ||||||
|             bpy.types.TOPBAR_MT_file_new.draw_ex(col1, context, use_splash=True) |             bpy.types.TOPBAR_MT_file_new.draw_ex(col1, context, use_splash=True) | ||||||
|         colB = ct_split.column() |         colB = ct_split.column() | ||||||
|         colB.menu("CT_MT_splash_mode", icon='DOWNARROW_HLT', text="") |         colB.menu("CT_MT_splash_mode", icon='DOWNARROW_HLT', text="") | ||||||
| @ -71,46 +71,3 @@ class WM_MT_splash(bpy.types.Menu): | |||||||
|             self.layout.label(text="Running in Offline Mode", icon='INTERNET_OFFLINE') |             self.layout.label(text="Running in Offline Mode", icon='INTERNET_OFFLINE') | ||||||
| 
 | 
 | ||||||
|         layout.separator() |         layout.separator() | ||||||
| 
 |  | ||||||
| class CT_MT_splash_mode(bpy.types.Menu): |  | ||||||
|     bl_idname = "CT_MT_splash_mode" |  | ||||||
|     bl_label = "Custom Templates Switch" |  | ||||||
|     bl_description = "Swtich between Blender's default and your Custom Templates" |  | ||||||
| 
 |  | ||||||
|     def draw(self, context): |  | ||||||
|         layout = self.layout |  | ||||||
|         prefs = context.preferences.addons[base_package].preferences |  | ||||||
|         def_check = 'NONE' |  | ||||||
|         ct_check = 'NONE' |  | ||||||
|         if prefs.override_splash: |  | ||||||
|             ct_check = "CHECKMARK" |  | ||||||
|         else: |  | ||||||
|             def_check = "CHECKMARK" |  | ||||||
|              |  | ||||||
|         layout.operator("ct.open_preferences", text="Manage templates") |  | ||||||
|         layout.separator() |  | ||||||
|         layout.operator("ct.splash_custom", text="Use Custom Templates", icon=ct_check) |  | ||||||
|         layout.operator("ct.splash_default", text="Use Blender's Default", icon=def_check) |  | ||||||
|          |  | ||||||
| class CT_OT_splash_default(bpy.types.Operator): |  | ||||||
|     bl_idname = "ct.splash_default" |  | ||||||
|     bl_label = "Display default Blender's templates" |  | ||||||
|     bl_description = "Use Blender's default templates in the Splash Screen" |  | ||||||
| 
 |  | ||||||
|     def execute(self, context): |  | ||||||
|         prefs = context.preferences.addons[base_package].preferences |  | ||||||
|         prefs.override_splash = False |  | ||||||
|         context.preferences.is_dirty = True |  | ||||||
|         return {'FINISHED'} |  | ||||||
|      |  | ||||||
| class CT_OT_splash_custom(bpy.types.Operator): |  | ||||||
|     bl_idname = "ct.splash_custom" |  | ||||||
|     bl_label = "Display your custom templates" |  | ||||||
|     bl_description = "Use your custom templates in the Splash Screen (limited to first 5)" |  | ||||||
| 
 |  | ||||||
|     def execute(self, context): |  | ||||||
|         prefs = context.preferences.addons[base_package].preferences |  | ||||||
|         prefs.override_splash = True |  | ||||||
|         context.preferences.is_dirty = True |  | ||||||
|         return {'FINISHED'} |  | ||||||
|      |  | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user