From b61939a2b7fd9843b100f02266c1f5e4f719a2b3 Mon Sep 17 00:00:00 2001 From: cantunborn Date: Thu, 12 Mar 2026 12:44:41 -0700 Subject: [PATCH 1/2] Fix macOS .app bundle support for game launch .app bundles are directories on macOS and could not be selected in the file picker or validated/launched as executables. Treat .app dirs as files in the picker, accept them in validate_executables(), and launch them via the macOS `open` command in _launch_exe(). Co-Authored-By: Claude Sonnet 4.6 --- common/structs.py | 12 +++++++++--- external/filepicker.py | 3 ++- modules/callbacks.py | 3 +++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/common/structs.py b/common/structs.py index d3f2189d..f56de734 100644 --- a/common/structs.py +++ b/common/structs.py @@ -1036,16 +1036,22 @@ def validate_executables(self): if base in exe.parents: self.executables[i] = exe.relative_to(base).as_posix() changed = True - executables_valids.append(exe.is_file()) + executables_valids.append(exe.is_file() or (globals.os is Os.MacOS and exe.suffix == ".app" and exe.is_dir())) else: - executables_valids.append((base / exe).is_file()) + abs_exe = base / exe + executables_valids.append(abs_exe.is_file() or (globals.os is Os.MacOS and abs_exe.suffix == ".app" and abs_exe.is_dir())) self.executables_valids = executables_valids if changed: from external import async_thread from modules import db async_thread.run(db.update_game(self, "executables")) else: - self.executables_valids = [utils.is_uri(executable) or os.path.isfile(executable) for executable in self.executables] + def _exe_valid(executable): + if utils.is_uri(executable): + return True + exe = pathlib.Path(executable) + return exe.is_file() or (globals.os is Os.MacOS and exe.suffix == ".app" and exe.is_dir()) + self.executables_valids = [_exe_valid(e) for e in self.executables] self.executables_valid = all(self.executables_valids) if globals.gui: globals.gui.recalculate_ids = True diff --git a/external/filepicker.py b/external/filepicker.py index ad6c896b..d9153003 100644 --- a/external/filepicker.py +++ b/external/filepicker.py @@ -105,7 +105,8 @@ def refresh(self): items.sort(key=lambda item: item.name.lower()) # Sort alphabetically items.sort(key=lambda item: item.is_dir(), reverse=True) # Sort dirs first for item in items: - self.items.append((dir_icon if item.is_dir() else file_icon) + item.name) + is_app_bundle = sys.platform.startswith("darwin") and item.is_dir() and item.suffix == ".app" + self.items.append((file_icon if (not item.is_dir() or is_app_bundle) else dir_icon) + item.name) else: self.items.append("No items match your filter!" if self.filter_box_text else "This folder is empty!") except Exception: diff --git a/modules/callbacks.py b/modules/callbacks.py index 1f65d3cc..850a2e55 100644 --- a/modules/callbacks.py +++ b/modules/callbacks.py @@ -178,6 +178,9 @@ async def _launch_exe(executable: str): exe = pathlib.Path(executable) if globals.settings.default_exe_dir.get(globals.os) and not exe.is_absolute(): exe = pathlib.Path(globals.settings.default_exe_dir.get(globals.os)) / exe + if globals.os is Os.MacOS and exe.suffix == ".app" and exe.is_dir(): + await default_open(str(exe)) + return if not exe.is_file(): raise FileNotFoundError() From 30766391213056992f1142dcbb68f1252c66f28f Mon Sep 17 00:00:00 2001 From: WillyJL Date: Mon, 8 Jun 2026 22:11:27 +0200 Subject: [PATCH 2/2] Prioritize file icon on items that are neither dirs nor files --- external/filepicker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/filepicker.py b/external/filepicker.py index d9153003..0c5a3fd1 100644 --- a/external/filepicker.py +++ b/external/filepicker.py @@ -106,7 +106,7 @@ def refresh(self): items.sort(key=lambda item: item.is_dir(), reverse=True) # Sort dirs first for item in items: is_app_bundle = sys.platform.startswith("darwin") and item.is_dir() and item.suffix == ".app" - self.items.append((file_icon if (not item.is_dir() or is_app_bundle) else dir_icon) + item.name) + self.items.append((dir_icon if (item.is_dir() and not is_app_bundle) else file_icon) + item.name) else: self.items.append("No items match your filter!" if self.filter_box_text else "This folder is empty!") except Exception: