diff --git a/.gitignore b/.gitignore index 9825d7a9..fd12adbb 100644 --- a/.gitignore +++ b/.gitignore @@ -130,3 +130,4 @@ dmypy.json # operating system files .DS_Store .mcp.json +/.claude diff --git a/preferences/preferences.py b/preferences/preferences.py index f7c6344f..26394529 100644 --- a/preferences/preferences.py +++ b/preferences/preferences.py @@ -21,6 +21,7 @@ from ..ui.properties_panels import VIEW3D_PT_collision_visibility_panel from ..ui.properties_panels import label_multiline from ..ui.properties_panels import collider_presets_folder +from ..ui.properties_panels import get_permission_fix_url collection_colors = [ ("NONE", "White", "Default collection color", "OUTLINER_COLLECTION", 0), @@ -256,7 +257,7 @@ def draw_naming_panel(self, layout): row.operator(COLLISION_preset.bl_idname, text="", icon='REMOVE').remove_active = True row.operator("wm.url_open", text="", icon='HELP').url = "https://weisl.github.io/collider_import_engines/" - row.operator("wm.path_open", text='', icon='FILE_FOLDER').filepath = collider_presets_folder() + row.operator("simple_collider.open_folder", text='', icon='FILE_FOLDER').directory = collider_presets_folder() box_name = box.box() row = box.row() @@ -408,8 +409,8 @@ def draw_vhacd_panel(self, layout, context): "wm.url_open", text="How to Fix", icon='URL' - ).url = "https://weisl.github.io/collider_auto_convex/#fix-linux-permission" - row.operator("wm.path_open", text='Open Folder', icon='FILE_FOLDER').filepath = os.path.dirname(self.default_executable_path) + ).url = get_permission_fix_url() + row.operator("simple_collider.open_folder", text='Open Folder', icon='FILE_FOLDER').directory = os.path.dirname(self.default_executable_path) row = box.row() row.label(text="Custom Executable (Optional):") @@ -447,8 +448,8 @@ def draw_vhacd_panel(self, layout, context): "wm.url_open", text="How to Fix", icon='URL' - ).url = "https://weisl.github.io/collider_auto_convex/#fix-linux-permission" - row.operator("wm.path_open", text='Open Folder', icon='FILE_FOLDER').filepath = os.path.dirname(self.coacd_default_executable_path) + ).url = get_permission_fix_url() + row.operator("simple_collider.open_folder", text='Open Folder', icon='FILE_FOLDER').directory = os.path.dirname(self.coacd_default_executable_path) row = box.row() row.label(text="Custom Executable (Optional):") diff --git a/ui/__init__.py b/ui/__init__.py index 42bc176c..803ffeb3 100644 --- a/ui/__init__.py +++ b/ui/__init__.py @@ -9,6 +9,7 @@ classes = ( properties_panels.COLLSION_OT_open_directory_new, + properties_panels.COLLIDER_OT_open_folder, properties_panels.OBJECT_MT_collision_presets, properties_panels.PREFERENCES_OT_open_addon, properties_panels.BUTTON_OT_auto_convex, diff --git a/ui/popup.py b/ui/popup.py index 9b3c307f..490a9d49 100644 --- a/ui/popup.py +++ b/ui/popup.py @@ -5,6 +5,7 @@ from .properties_panels import draw_auto_convex_settings from .properties_panels import draw_auto_convex_coacd_settings +from .properties_panels import get_permission_fix_url class VIEW3D_PT_auto_convex_popup(bpy.types.Panel): @@ -35,7 +36,7 @@ def draw(self, context): "wm.url_open", text="Solution", icon='URL' - ).url = "https://weisl.github.io/collider_auto_convex/#fix-linux-permission" + ).url = get_permission_fix_url() return @@ -67,5 +68,5 @@ def draw(self, context): "wm.url_open", text="Solution", icon='URL' - ).url = "https://weisl.github.io/collider_auto_convex/#fix-linux-permission" + ).url = get_permission_fix_url() return diff --git a/ui/properties_panels.py b/ui/properties_panels.py index 149ed5f8..b2b7e644 100644 --- a/ui/properties_panels.py +++ b/ui/properties_panels.py @@ -1,5 +1,6 @@ import os import platform +import subprocess import textwrap import bpy @@ -12,6 +13,51 @@ import bpy + +def get_permission_fix_url(): + """URL to the docs section explaining how to grant execute permission for the platform.""" + anchor = 'fix-macos-permission' if platform.system() == 'Darwin' else 'fix-linux-permission' + return f"https://weisl.github.io/simple_collider/collider_auto_convex/#{anchor}" + + +class COLLIDER_OT_open_folder(bpy.types.Operator): + """Open a folder in the system file browser""" + bl_idname = "simple_collider.open_folder" + bl_label = "Open Folder" + bl_description = "Open the folder in the system file browser" + + directory: bpy.props.StringProperty(options={'HIDDEN'}) + + def execute(self, context): + if not self.directory or not os.path.isdir(self.directory): + self.report({'ERROR'}, f"Folder not found: {self.directory}") + return {'CANCELLED'} + + # "wm.path_open" is preferred, but some OS/Blender combinations resolve + # it to a backing operator without a `filepath` property (e.g. Blender + # 5.2 on Windows, see https://github.com/Weisl/simple_collider/issues/656), + # which raises TypeError for the unrecognized keyword. Fall back to + # opening the folder via the OS directly in that case. + try: + bpy.ops.wm.path_open(filepath=self.directory) + return {'FINISHED'} + except (TypeError, RuntimeError): + pass + + try: + if platform.system() == 'Windows': + os.startfile(self.directory) + elif platform.system() == 'Darwin': + subprocess.run(['open', self.directory], check=True) + else: + subprocess.run(['xdg-open', self.directory], check=True) + except (OSError, subprocess.SubprocessError) as error: + self.report({'ERROR'}, f"Could not open folder: {error}") + return {'CANCELLED'} + + return {'FINISHED'} + + class OBJECT_OT_set_default_collision_preset(bpy.types.Operator): """Set the selected preset as the default""" bl_idname = "object.set_default_collision_preset" @@ -403,7 +449,7 @@ def draw_naming_presets(self, context): filepath = collider_presets_folder() if filepath: - row.operator("wm.path_open", text='', icon='FILE_FOLDER').filepath = filepath + row.operator("simple_collider.open_folder", text='', icon='FILE_FOLDER').directory = filepath op = row.operator("simple_collider.open_preferences", text="", icon='PREFERENCES') op.addon_name = addon_name