Implement save file dialog functionality across platforms
- Added ShowSaveFileDialog and its platform-specific implementations for Windows and macOS, allowing users to save files with specified names and extensions. - Enhanced the FileDialogWrapper class to support both native and bespoke dialog options based on feature flags. - Updated the editor manager to utilize the new save dialog for ROM and project file saving, improving user experience and file management. - Refactored existing dialog methods to ensure consistency and maintainability across different platforms.
This commit is contained in:
@@ -173,18 +173,92 @@ std::string ShowOpenFileDialogImpl() {
|
||||
return file_path_windows;
|
||||
}
|
||||
|
||||
// Forward declaration for folder dialog implementation
|
||||
// Forward declarations for dialog implementations
|
||||
std::string ShowOpenFolderDialogImpl();
|
||||
std::string ShowSaveFileDialogImpl(const std::string& default_name, const std::string& default_extension);
|
||||
|
||||
std::string FileDialogWrapper::ShowOpenFolderDialog() {
|
||||
return ShowOpenFolderDialogImpl();
|
||||
}
|
||||
|
||||
std::string FileDialogWrapper::ShowSaveFileDialog(const std::string& default_name,
|
||||
const std::string& default_extension) {
|
||||
if (FeatureFlags::get().kUseNativeFileDialog) {
|
||||
return ShowSaveFileDialogNFD(default_name, default_extension);
|
||||
} else {
|
||||
return ShowSaveFileDialogBespoke(default_name, default_extension);
|
||||
}
|
||||
}
|
||||
|
||||
std::string FileDialogWrapper::ShowOpenFolderDialogNFD() {
|
||||
// Windows doesn't use NFD in this implementation, fallback to bespoke
|
||||
return ShowOpenFolderDialogBespoke();
|
||||
}
|
||||
|
||||
std::string FileDialogWrapper::ShowSaveFileDialogNFD(const std::string& default_name,
|
||||
const std::string& default_extension) {
|
||||
// Windows doesn't use NFD in this implementation, fallback to bespoke
|
||||
return ShowSaveFileDialogBespoke(default_name, default_extension);
|
||||
}
|
||||
|
||||
std::string FileDialogWrapper::ShowSaveFileDialogBespoke(const std::string& default_name,
|
||||
const std::string& default_extension) {
|
||||
return ShowSaveFileDialogImpl(default_name, default_extension);
|
||||
}
|
||||
|
||||
std::string ShowSaveFileDialogImpl(const std::string& default_name,
|
||||
const std::string& default_extension) {
|
||||
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
||||
|
||||
IFileSaveDialog *pfd = NULL;
|
||||
HRESULT hr = CoCreateInstance(CLSID_FileSaveDialog, NULL, CLSCTX_ALL, IID_IFileSaveDialog,
|
||||
reinterpret_cast<void **>(&pfd));
|
||||
std::string file_path_windows;
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
// Set default filename if provided
|
||||
if (!default_name.empty()) {
|
||||
std::wstring wide_name(default_name.begin(), default_name.end());
|
||||
pfd->SetFileName(wide_name.c_str());
|
||||
}
|
||||
|
||||
// Set file type filters if extension provided
|
||||
if (!default_extension.empty()) {
|
||||
if (default_extension == "theme") {
|
||||
COMDLG_FILTERSPEC filters[] = { {L"Theme Files", L"*.theme"}, {L"All Files", L"*.*"} };
|
||||
pfd->SetFileTypes(2, filters);
|
||||
} else if (default_extension == "yaze") {
|
||||
COMDLG_FILTERSPEC filters[] = { {L"YAZE Project Files", L"*.yaze"}, {L"All Files", L"*.*"} };
|
||||
pfd->SetFileTypes(2, filters);
|
||||
} else if (default_extension == "sfc" || default_extension == "smc") {
|
||||
COMDLG_FILTERSPEC filters[] = { {L"ROM Files", L"*.sfc;*.smc"}, {L"All Files", L"*.*"} };
|
||||
pfd->SetFileTypes(2, filters);
|
||||
}
|
||||
}
|
||||
|
||||
// Show the dialog
|
||||
hr = pfd->Show(NULL);
|
||||
if (SUCCEEDED(hr)) {
|
||||
IShellItem *psiResult;
|
||||
hr = pfd->GetResult(&psiResult);
|
||||
if (SUCCEEDED(hr)) {
|
||||
// Get the file path
|
||||
PWSTR pszFilePath;
|
||||
psiResult->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
|
||||
char str[512];
|
||||
wcstombs(str, pszFilePath, 512);
|
||||
file_path_windows = str;
|
||||
psiResult->Release();
|
||||
CoTaskMemFree(pszFilePath);
|
||||
}
|
||||
}
|
||||
pfd->Release();
|
||||
}
|
||||
|
||||
CoUninitialize();
|
||||
return file_path_windows;
|
||||
}
|
||||
|
||||
std::string FileDialogWrapper::ShowOpenFolderDialogBespoke() {
|
||||
return ShowOpenFolderDialogImpl();
|
||||
}
|
||||
@@ -306,10 +380,66 @@ std::string FileDialogWrapper::ShowOpenFileDialogNFD() {
|
||||
|
||||
std::string FileDialogWrapper::ShowOpenFileDialogBespoke() {
|
||||
// Implement bespoke file dialog or return placeholder
|
||||
// This would contain the custom macOS implementation
|
||||
// This would contain the custom Linux implementation
|
||||
return ""; // Placeholder for bespoke implementation
|
||||
}
|
||||
|
||||
std::string FileDialogWrapper::ShowSaveFileDialogNFD(const std::string& default_name,
|
||||
const std::string& default_extension) {
|
||||
#if defined(YAZE_ENABLE_NFD) && YAZE_ENABLE_NFD
|
||||
NFD_Init();
|
||||
nfdu8char_t *out_path = NULL;
|
||||
|
||||
nfdsavedialogu8args_t args = {0};
|
||||
if (!default_extension.empty()) {
|
||||
// Create filter for the save dialog
|
||||
static nfdu8filteritem_t filters[3] = {
|
||||
{"Theme File", "theme"},
|
||||
{"Project File", "yaze"},
|
||||
{"ROM File", "sfc,smc"}
|
||||
};
|
||||
|
||||
if (default_extension == "theme") {
|
||||
args.filterList = &filters[0];
|
||||
args.filterCount = 1;
|
||||
} else if (default_extension == "yaze") {
|
||||
args.filterList = &filters[1];
|
||||
args.filterCount = 1;
|
||||
} else if (default_extension == "sfc" || default_extension == "smc") {
|
||||
args.filterList = &filters[2];
|
||||
args.filterCount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!default_name.empty()) {
|
||||
args.defaultName = default_name.c_str();
|
||||
}
|
||||
|
||||
nfdresult_t result = NFD_SaveDialogU8_With(&out_path, &args);
|
||||
if (result == NFD_OKAY) {
|
||||
std::string file_path(out_path);
|
||||
NFD_FreePath(out_path);
|
||||
NFD_Quit();
|
||||
return file_path;
|
||||
} else if (result == NFD_CANCEL) {
|
||||
NFD_Quit();
|
||||
return "";
|
||||
}
|
||||
NFD_Quit();
|
||||
return "";
|
||||
#else
|
||||
// NFD not available - fallback to bespoke
|
||||
return ShowSaveFileDialogBespoke(default_name, default_extension);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string FileDialogWrapper::ShowSaveFileDialogBespoke(const std::string& default_name,
|
||||
const std::string& default_extension) {
|
||||
// Placeholder for Linux implementation
|
||||
// Could use zenity or similar system dialogs
|
||||
return ""; // For now return empty - can be implemented later
|
||||
}
|
||||
|
||||
std::string FileDialogWrapper::ShowOpenFolderDialog() {
|
||||
// Use global feature flag to choose implementation
|
||||
if (FeatureFlags::get().kUseNativeFileDialog) {
|
||||
|
||||
@@ -20,10 +20,21 @@ class FileDialogWrapper {
|
||||
* folder path. Uses global feature flag to choose implementation.
|
||||
*/
|
||||
static std::string ShowOpenFolderDialog();
|
||||
|
||||
/**
|
||||
* @brief ShowSaveFileDialog opens a save file dialog and returns the selected
|
||||
* filepath. Uses global feature flag to choose implementation.
|
||||
*/
|
||||
static std::string ShowSaveFileDialog(const std::string& default_name = "",
|
||||
const std::string& default_extension = "");
|
||||
|
||||
// Specific implementations for testing
|
||||
static std::string ShowOpenFileDialogNFD();
|
||||
static std::string ShowOpenFileDialogBespoke();
|
||||
static std::string ShowSaveFileDialogNFD(const std::string& default_name = "",
|
||||
const std::string& default_extension = "");
|
||||
static std::string ShowSaveFileDialogBespoke(const std::string& default_name = "",
|
||||
const std::string& default_extension = "");
|
||||
static std::string ShowOpenFolderDialogNFD();
|
||||
static std::string ShowOpenFolderDialogBespoke();
|
||||
static std::vector<std::string> GetSubdirectoriesInFolder(
|
||||
|
||||
@@ -88,6 +88,28 @@ std::string yaze::core::FileDialogWrapper::ShowOpenFileDialogBespoke() {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string yaze::core::FileDialogWrapper::ShowSaveFileDialogBespoke(const std::string& default_name,
|
||||
const std::string& default_extension) {
|
||||
NSSavePanel* savePanel = [NSSavePanel savePanel];
|
||||
|
||||
if (!default_name.empty()) {
|
||||
[savePanel setNameFieldStringValue:[NSString stringWithUTF8String:default_name.c_str()]];
|
||||
}
|
||||
|
||||
if (!default_extension.empty()) {
|
||||
NSString* ext = [NSString stringWithUTF8String:default_extension.c_str()];
|
||||
[savePanel setAllowedFileTypes:@[ext]];
|
||||
}
|
||||
|
||||
if ([savePanel runModal] == NSModalResponseOK) {
|
||||
NSURL* url = [savePanel URL];
|
||||
NSString* path = [url path];
|
||||
return std::string([path UTF8String]);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
// Global feature flag-based dispatch methods
|
||||
std::string yaze::core::FileDialogWrapper::ShowOpenFileDialog() {
|
||||
if (FeatureFlags::get().kUseNativeFileDialog) {
|
||||
@@ -105,6 +127,15 @@ std::string yaze::core::FileDialogWrapper::ShowOpenFolderDialog() {
|
||||
}
|
||||
}
|
||||
|
||||
std::string yaze::core::FileDialogWrapper::ShowSaveFileDialog(const std::string& default_name,
|
||||
const std::string& default_extension) {
|
||||
if (FeatureFlags::get().kUseNativeFileDialog) {
|
||||
return ShowSaveFileDialogNFD(default_name, default_extension);
|
||||
} else {
|
||||
return ShowSaveFileDialogBespoke(default_name, default_extension);
|
||||
}
|
||||
}
|
||||
|
||||
// NFD implementation for macOS (fallback to bespoke if NFD not available)
|
||||
std::string yaze::core::FileDialogWrapper::ShowOpenFileDialogNFD() {
|
||||
#if defined(YAZE_ENABLE_NFD) && YAZE_ENABLE_NFD
|
||||
@@ -156,6 +187,55 @@ std::string yaze::core::FileDialogWrapper::ShowOpenFolderDialogNFD() {
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string yaze::core::FileDialogWrapper::ShowSaveFileDialogNFD(const std::string& default_name,
|
||||
const std::string& default_extension) {
|
||||
#if defined(YAZE_ENABLE_NFD) && YAZE_ENABLE_NFD
|
||||
NFD_Init();
|
||||
nfdu8char_t *out_path = NULL;
|
||||
|
||||
nfdsavedialogu8args_t args = {0};
|
||||
if (!default_extension.empty()) {
|
||||
// Create filter for the save dialog
|
||||
static nfdu8filteritem_t filters[3] = {
|
||||
{"Theme File", "theme"},
|
||||
{"Project File", "yaze"},
|
||||
{"ROM File", "sfc,smc"}
|
||||
};
|
||||
|
||||
if (default_extension == "theme") {
|
||||
args.filterList = &filters[0];
|
||||
args.filterCount = 1;
|
||||
} else if (default_extension == "yaze") {
|
||||
args.filterList = &filters[1];
|
||||
args.filterCount = 1;
|
||||
} else if (default_extension == "sfc" || default_extension == "smc") {
|
||||
args.filterList = &filters[2];
|
||||
args.filterCount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!default_name.empty()) {
|
||||
args.defaultName = default_name.c_str();
|
||||
}
|
||||
|
||||
nfdresult_t result = NFD_SaveDialogU8_With(&out_path, &args);
|
||||
if (result == NFD_OKAY) {
|
||||
std::string file_path(out_path);
|
||||
NFD_FreePath(out_path);
|
||||
NFD_Quit();
|
||||
return file_path;
|
||||
} else if (result == NFD_CANCEL) {
|
||||
NFD_Quit();
|
||||
return "";
|
||||
}
|
||||
NFD_Quit();
|
||||
return "";
|
||||
#else
|
||||
// NFD not compiled in, use bespoke
|
||||
return ShowSaveFileDialogBespoke(default_name, default_extension);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string yaze::core::FileDialogWrapper::ShowOpenFolderDialogBespoke() {
|
||||
NSOpenPanel* openPanel = [NSOpenPanel openPanel];
|
||||
[openPanel setCanChooseFiles:NO];
|
||||
|
||||
Reference in New Issue
Block a user