Add file browser support on Linux through portable-file-dialogs

This commit is contained in:
Henrik Rydgård 2025-03-29 08:43:30 +01:00
parent fe4ca6fb63
commit 5f8f40e592
5 changed files with 106 additions and 11 deletions

View file

@ -104,6 +104,7 @@ enum class BrowseFileType {
SOUND_EFFECT,
ZIP,
SYMBOL_MAP,
SYMBOL_MAP_NOCASH,
ATRAC3,
ANY,
};

View file

@ -24,6 +24,8 @@ SDLJoystick *joystick = NULL;
#include <thread>
#include <locale>
#include "ext/portable-file-dialogs/portable-file-dialogs.h"
#include "Common/System/Display.h"
#include "Common/System/System.h"
#include "Common/System/Request.h"
@ -221,6 +223,47 @@ void System_Vibrate(int length_ms) {
// Ignore on PC
}
static void InitializeFilters(std::vector<std::string> &filters, BrowseFileType type) {
switch (type) {
case BrowseFileType::BOOTABLE:
filters.push_back("All supported file types (*.iso *.cso *.chd *.pbp *.elf *.prx *.zip *.ppdmp)");
filters.push_back("*.pbp *.elf *.iso *.cso *.chd *.prx *.zip *.ppdmp");
break;
case BrowseFileType::INI:
filters.push_back("Ini files");
filters.push_back("*.ini");
break;
case BrowseFileType::ZIP:
filters.push_back("ZIP files");
filters.push_back("*.zip");
break;
case BrowseFileType::DB:
filters.push_back("Cheat db files");
filters.push_back("*.db");
break;
case BrowseFileType::SOUND_EFFECT:
filters.push_back("Sound effect files (wav, mp3)");
filters.push_back("*.wav *.mp3");
break;
case BrowseFileType::SYMBOL_MAP:
filters.push_back("PPSSPP Symbol Map files (ppmap)");
filters.push_back("*.ppmap");
break;
case BrowseFileType::SYMBOL_MAP_NOCASH:
filters.push_back("No$ symbol Map files (sym)");
filters.push_back("*.sym");
break;
case BrowseFileType::ATRAC3:
filters.push_back("Atrac3 files (at3)");
filters.push_back("*.at3");
break;
case BrowseFileType::ANY:
break;
}
filters.push_back("All files (*.*)");
filters.push_back("*");
}
bool System_MakeRequest(SystemRequestType type, int requestId, const std::string &param1, const std::string &param2, int64_t param3, int64_t param4) {
switch (type) {
case SystemRequestType::RESTART_APP:
@ -288,6 +331,44 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string
DarwinFileSystemServices::presentDirectoryPanel(callback, /* allowFiles = */ false, /* allowDirectories = */ true);
return true;
}
#else
case SystemRequestType::BROWSE_FOR_FILE:
case SystemRequestType::BROWSE_FOR_FILE_SAVE:
{
// TODO: Add non-blocking support.
const BrowseFileType browseType = (BrowseFileType)param3;
std::string initialFilename = param2;
const std::string &title = param1;
std::vector<std::string> filters;
InitializeFilters(filters, browseType);
if (type == SystemRequestType::BROWSE_FOR_FILE) {
std::vector<std::string> result = pfd::open_file(title, initialFilename, filters).result();
if (!result.empty()) {
g_requestManager.PostSystemSuccess(requestId, result[0]);
} else {
g_requestManager.PostSystemFailure(requestId);
}
} else {
std::string result = pfd::save_file(title, initialFilename, filters).result();
if (!result.empty()) {
g_requestManager.PostSystemSuccess(requestId, result);
} else {
g_requestManager.PostSystemFailure(requestId);
}
}
return true;
}
case SystemRequestType::BROWSE_FOR_FOLDER:
{
// TODO: Add non-blocking support.
std::string result = pfd::select_folder(param1, param2).result();
if (!result.empty()) {
g_requestManager.PostSystemSuccess(requestId, result);
} else {
g_requestManager.PostSystemFailure(requestId);
}
return true;
}
#endif
case SystemRequestType::TOGGLE_FULLSCREEN_STATE:
{
@ -606,16 +687,18 @@ bool System_GetPropertyBool(SystemProperty prop) {
#endif
case SYSPROP_CAN_JIT:
return true;
case SYSPROP_SUPPORTS_OPEN_FILE_IN_EDITOR:
case SYSPROP_SUPPORTS_OPEN_FILE_IN_EDITOR:
return true; // FileUtil.cpp: OpenFileInEditor
#ifndef HTTPS_NOT_AVAILABLE
case SYSPROP_SUPPORTS_HTTPS:
return !g_Config.bDisableHTTPS;
#endif
case SYSPROP_HAS_FOLDER_BROWSER:
case SYSPROP_HAS_FILE_BROWSER:
#if PPSSPP_PLATFORM(MAC)
case SYSPROP_HAS_FOLDER_BROWSER:
case SYSPROP_HAS_FILE_BROWSER:
return true;
#else
return pfd::settings::available();
#endif
case SYSPROP_HAS_ACCELEROMETER:
#if defined(MOBILE_DEVICE)
@ -1501,7 +1584,7 @@ int main(int argc, char *argv[]) {
graphicsContext->ThreadStart();
InputStateTracker inputTracker{};
#if PPSSPP_PLATFORM(MAC)
// setup menu items for macOS
initializeOSXExtras();

View file

@ -88,7 +88,13 @@ void DarwinFileSystemServices::presentDirectoryPanel(
[panel setAllowedFileTypes:[NSArray arrayWithObject:@"db"]];
break;
case BrowseFileType::SOUND_EFFECT:
[panel setAllowedFileTypes:[NSArray arrayWithObject:@"wav"]];
[panel setAllowedFileTypes:[NSArray arrayWithObject:@"wav", @"mp3"]];
break;
case BrowseFileType::SYMBOL_MAP:
[panel setAllowedFileTypes:[NSArray arrayWithObject:@"ppsym"]];
break;
case BrowseFileType::SYMBOL_MAP_NOCASH:
[panel setAllowedFileTypes:[NSArray arrayWithObject:@"sym"]];
break;
case BrowseFileType::ATRAC3:
[panel setAllowedFileTypes:[NSArray arrayWithObject:@"at3"]];
@ -113,21 +119,21 @@ void DarwinFileSystemServices::presentDirectoryPanel(
UIViewController *rootViewController = UIApplication.sharedApplication
.keyWindow
.rootViewController;
// get current window view controller
if (!rootViewController)
return;
NSMutableArray<NSString *> *types = [NSMutableArray array];
UIDocumentPickerMode pickerMode = UIDocumentPickerModeOpen;
if (allowDirectories)
[types addObject: (__bridge NSString *)kUTTypeFolder];
if (allowFiles) {
[types addObject: (__bridge NSString *)kUTTypeItem];
pickerMode = UIDocumentPickerModeImport;
}
UIDocumentPickerViewController *pickerVC = [[UIDocumentPickerViewController alloc] initWithDocumentTypes: types inMode: pickerMode];
// What if you wanted to go to heaven, but then God showed you the next few lines?
// serious note: have to do this, because __pickerDelegate has to stay retained as a class property
@ -142,7 +148,7 @@ Path DarwinFileSystemServices::appropriateMemoryStickDirectoryToUse() {
NSString *userPreferred = [[NSUserDefaults standardUserDefaults] stringForKey:@(PreferredMemoryStickUserDefaultsKey)];
if (userPreferred)
return Path(userPreferred.UTF8String);
return defaultMemoryStickPath();
}

View file

@ -436,7 +436,7 @@ bool System_GetPropertyBool(SystemProperty prop) {
return true;
case SYSPROP_HAS_KEYBOARD:
{
// Do actual check
// Do actual check
// touch devices has input pane, we need to depend on it
// I don't know any possible way to display input dialog in non-xaml apps
return isKeyboardAvailable() || isTouchAvailable();
@ -519,6 +519,9 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string
case BrowseFileType::SYMBOL_MAP:
supportedExtensions = { ".ppmap" };
break;
case BrowseFileType::SYMBOL_MAP_NOCASH:
supportedExtensions = { ".sym" };
break;
case BrowseFileType::DB:
supportedExtensions = { ".db" };
break;

View file

@ -539,6 +539,8 @@ static std::wstring MakeWindowsFilter(BrowseFileType type) {
return FinalizeFilter(L"Sound effect files (*.wav *.mp3)|*.wav;*.mp3|All files (*.*)|*.*||");
case BrowseFileType::SYMBOL_MAP:
return FinalizeFilter(L"Symbol map files (*.ppmap)|*.ppmap|All files (*.*)|*.*||");
case BrowseFileType::SYMBOL_MAP_NOCASH:
return FinalizeFilter(L"No$ symbol map files (*.sym)|*.sym|All files (*.*)|*.*||");
case BrowseFileType::ATRAC3:
return FinalizeFilter(L"ATRAC3/3+ files (*.at3)|*.at3|All files (*.*)|*.*||");
case BrowseFileType::ANY: