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).
|
||||||
|
@ -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_OT_export_templates,
|
|
||||||
CT_OT_import_templates,
|
|
||||||
CT_OT_splash_custom,
|
|
||||||
CT_OT_splash_default,
|
|
||||||
CT_OT_open_preferences,
|
|
||||||
CT_MT_splash_mode,
|
CT_MT_splash_mode,
|
||||||
CT_MT_templates_menu,
|
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_import_templates,
|
||||||
|
CT_OT_open_preferences,
|
||||||
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