2
0
mirror of https://github.com/Laupetin/OpenAssetTools.git synced 2025-11-18 03:02:07 +00:00

chore: implement dialog handler for linux

This commit is contained in:
Jan Laupetin
2025-10-09 23:25:04 +02:00
parent 5bbeaed3a3
commit 210941991e
7 changed files with 252 additions and 245 deletions

View File

@@ -5,8 +5,6 @@
#include "Json/JsonExtension.h" #include "Json/JsonExtension.h"
using namespace PLATFORM_NAMESPACE;
namespace namespace
{ {
class FileDialogFilterDto class FileDialogFilterDto
@@ -34,9 +32,12 @@ namespace
NLOHMANN_DEFINE_TYPE_EXTENSION(SaveFileDialogDto, filters); NLOHMANN_DEFINE_TYPE_EXTENSION(SaveFileDialogDto, filters);
void ReplyWithDialogResult(webview::webview& wv, const std::string& id, const DialogCallbackResultType resultType, const std::optional<std::string>& result) void ReplyWithDialogResult(webview::webview& wv,
const std::string& id,
const ui::DialogCallbackResultType resultType,
const std::optional<std::string>& result)
{ {
if (resultType == FAILED) if (resultType == ui::DialogCallbackResultType::FAILED)
ui::PromiseReject(wv, id, result); ui::PromiseReject(wv, id, result);
else else
ui::PromiseResolve(wv, id, result); ui::PromiseResolve(wv, id, result);
@@ -44,9 +45,9 @@ namespace
void OpenFileDialogBind(webview::webview& wv, const std::string& id, const std::optional<OpenFileDialogDto>& dto) void OpenFileDialogBind(webview::webview& wv, const std::string& id, const std::optional<OpenFileDialogDto>& dto)
{ {
OpenFileDialog dialog; ui::OpenFileDialog dialog;
dialog.SetCallback( dialog.SetCallback(
[&wv, id](const DialogCallbackResultType resultType, const std::optional<std::string>& result) [&wv, id](const ui::DialogCallbackResultType resultType, const std::optional<std::string>& result)
{ {
ReplyWithDialogResult(wv, id, resultType, result); ReplyWithDialogResult(wv, id, resultType, result);
}); });
@@ -64,9 +65,9 @@ namespace
void SaveFileDialogBind(webview::webview& wv, const std::string& id, const std::optional<SaveFileDialogDto>& dto) void SaveFileDialogBind(webview::webview& wv, const std::string& id, const std::optional<SaveFileDialogDto>& dto)
{ {
SaveFileDialog dialog; ui::SaveFileDialog dialog;
dialog.SetCallback( dialog.SetCallback(
[&wv, id](const DialogCallbackResultType resultType, const std::optional<std::string>& result) [&wv, id](const ui::DialogCallbackResultType resultType, const std::optional<std::string>& result)
{ {
ReplyWithDialogResult(wv, id, resultType, result); ReplyWithDialogResult(wv, id, resultType, result);
}); });
@@ -84,9 +85,9 @@ namespace
void FolderSelectDialogBind(webview::webview& wv, const std::string& id) void FolderSelectDialogBind(webview::webview& wv, const std::string& id)
{ {
FolderSelectDialog dialog; ui::FolderSelectDialog dialog;
dialog.SetCallback( dialog.SetCallback(
[&wv, id](const DialogCallbackResultType resultType, const std::optional<std::string>& result) [&wv, id](const ui::DialogCallbackResultType resultType, const std::optional<std::string>& result)
{ {
ReplyWithDialogResult(wv, id, resultType, result); ReplyWithDialogResult(wv, id, resultType, result);
}); });

View File

@@ -0,0 +1,24 @@
#include "DialogHandler.h"
namespace ui
{
DialogWithCallback::DialogWithCallback() = default;
void DialogWithCallback::SetCallback(callback_t callback)
{
m_callback = std::move(callback);
}
FileDialogFilter::FileDialogFilter(std::string name, std::string filter)
: m_name(std::move(name)),
m_filter(std::move(filter))
{
}
FileDialog::FileDialog() = default;
void FileDialog::AddFilter(std::string name, std::string filter)
{
m_filters.emplace_back(std::move(name), std::move(filter));
}
} // namespace ui

View File

@@ -1,7 +1,74 @@
#pragma once #pragma once
#ifdef _WIN32 #include <cstdint>
#include "Windows/DialogHandlerWindows.h" #include <functional>
#elif defined(__linux__) #include <optional>
#include "Linux/DialogHandlerLinux.h" #include <string>
#endif #include <vector>
namespace ui
{
enum class DialogCallbackResultType : std::uint8_t
{
SUCCESSFUL,
CANCELLED,
FAILED
};
class DialogWithCallback
{
public:
using callback_t = std::function<void(DialogCallbackResultType resultType, std::optional<std::string> result)>;
DialogWithCallback();
void SetCallback(callback_t callback);
protected:
callback_t m_callback;
};
class FileDialogFilter
{
public:
FileDialogFilter(std::string name, std::string filter);
std::string m_name;
std::string m_filter;
};
class FileDialog : public DialogWithCallback
{
public:
FileDialog();
void AddFilter(std::string name, std::string filter);
protected:
std::vector<FileDialogFilter> m_filters;
};
class OpenFileDialog : public FileDialog
{
public:
OpenFileDialog();
void OpenAsync();
};
class SaveFileDialog : public FileDialog
{
public:
SaveFileDialog();
void OpenAsync();
};
class FolderSelectDialog : public DialogWithCallback
{
public:
FolderSelectDialog();
void OpenAsync();
};
} // namespace ui

View File

@@ -1,13 +1,33 @@
#include "DialogHandlerLinux.h" #include "Web/Platform/DialogHandler.h"
#ifdef __linux__ #ifdef __linux__
#include <cassert>
#include <cstdint>
#include <gtk/gtk.h> #include <gtk/gtk.h>
using namespace PLATFORM_NAMESPACE_LINUX;
namespace namespace
{ {
enum class DialogOperation : std::uint8_t
{
OPEN_FILE,
SAVE_FILE,
SELECT_FOLDER
};
class DialogData
{
public:
DialogData(DialogOperation operation, ui::DialogWithCallback::callback_t callback)
: m_operation(operation),
m_callback(std::move(callback))
{
}
DialogOperation m_operation;
ui::DialogWithCallback::callback_t m_callback;
};
bool InitGtk() bool InitGtk()
{ {
#if GTK_MAJOR_VERSION >= 4 #if GTK_MAJOR_VERSION >= 4
@@ -17,29 +37,77 @@ namespace
#endif #endif
} }
void OpenFileDialog() void OpenFileDialog() {}
{
#ifdef GDK_AVAILABLE_IN_4_10
auto* dialog = gtk_file_dialog_new();
gtk_file_dialog_open(dialog, nullptr, nullptr, [](GObject *source, void SetFilters(GtkFileDialog* pDialog, const std::vector<ui::FileDialogFilter>& filters)
GAsyncResult *result,
gpointer user_data
) -> void {
}, nullptr
);
g_object_unref(dialog);
#endif
}
bool SetFilters(void* pFileOpen, const std::vector<FileDialogFilter>& filters)
{ {
if (filters.empty()) if (filters.empty())
return true; return;
return false; auto* listStore = g_list_store_new(GTK_TYPE_FILE_FILTER);
for (auto& filter : filters)
{
auto* fileFilter = gtk_file_filter_new();
gtk_file_filter_set_name(fileFilter, filter.m_name.c_str());
gtk_file_filter_add_pattern(fileFilter, filter.m_filter.c_str());
g_list_store_append(listStore, fileFilter);
g_object_unref(fileFilter);
}
gtk_file_dialog_set_filters(pDialog, G_LIST_MODEL(listStore));
g_object_unref(listStore);
}
void OnDialogResult(GObject* source, GAsyncResult* asyncResult, gpointer userData)
{
auto* dialogData = reinterpret_cast<DialogData*>(userData);
GError* error = nullptr;
GFile* file = nullptr;
if (dialogData->m_operation == DialogOperation::OPEN_FILE)
{
file = gtk_file_dialog_open_finish(GTK_FILE_DIALOG(source), asyncResult, &error);
}
else if (dialogData->m_operation == DialogOperation::SAVE_FILE)
{
file = gtk_file_dialog_save_finish(GTK_FILE_DIALOG(source), asyncResult, &error);
}
else if (dialogData->m_operation == DialogOperation::SELECT_FOLDER)
{
file = gtk_file_dialog_select_folder_finish(GTK_FILE_DIALOG(source), asyncResult, &error);
}
else
{
assert(false);
}
std::optional<std::string> result;
ui::DialogCallbackResultType resultType;
if (error)
{
if (error->code == GTK_DIALOG_ERROR_DISMISSED)
resultType = ui::DialogCallbackResultType::CANCELLED;
else
resultType = ui::DialogCallbackResultType::FAILED;
g_error_free(error);
}
else
{
resultType = ui::DialogCallbackResultType::SUCCESSFUL;
result = std::string(g_file_get_path(file));
}
if (file)
{
g_object_unref(file);
}
dialogData->m_callback(resultType, result);
delete dialogData;
} }
std::optional<std::string> ShowFileDialog() std::optional<std::string> ShowFileDialog()
@@ -50,43 +118,60 @@ namespace
} }
} // namespace } // namespace
namespace PLATFORM_NAMESPACE_LINUX namespace ui
{ {
FileDialogFilter::FileDialogFilter(std::string name, std::string filter) #if GTK_MAJOR_VERSION >= 4
: m_name(std::move(name)),
m_filter(std::move(filter))
{
}
FileDialog::FileDialog() = default;
void FileDialog::AddFilter(std::string name, std::string filter)
{
m_filters.emplace_back(std::move(name), std::move(filter));
}
OpenFileDialog::OpenFileDialog() = default; OpenFileDialog::OpenFileDialog() = default;
std::optional<std::string> OpenFileDialog::Open() const void OpenFileDialog::OpenAsync()
{ {
if (!InitGtk()) if (!InitGtk())
return std::nullopt; return m_callback(DialogCallbackResultType::FAILED, std::nullopt);
return std::nullopt; auto* dialog = gtk_file_dialog_new();
SetFilters(dialog, m_filters);
// Move data out of the dialog object since it may be destroyed
auto* dialogData = new DialogData(DialogOperation::OPEN_FILE, std::move(m_callback));
gtk_file_dialog_open(dialog, nullptr, nullptr, OnDialogResult, dialogData);
g_object_unref(dialog);
} }
SaveFileDialog::SaveFileDialog() = default; SaveFileDialog::SaveFileDialog() = default;
std::optional<std::string> SaveFileDialog::Open() const void SaveFileDialog::OpenAsync()
{ {
return std::nullopt; if (!InitGtk())
return m_callback(DialogCallbackResultType::FAILED, std::nullopt);
auto* dialog = gtk_file_dialog_new();
SetFilters(dialog, m_filters);
// Move data out of the dialog object since it may be destroyed
auto* dialogData = new DialogData(DialogOperation::SAVE_FILE, std::move(m_callback));
gtk_file_dialog_save(dialog, nullptr, nullptr, OnDialogResult, dialogData);
g_object_unref(dialog);
} }
FolderSelectDialog::FolderSelectDialog() = default; FolderSelectDialog::FolderSelectDialog() = default;
std::optional<std::string> FolderSelectDialog::Open() const void FolderSelectDialog::OpenAsync()
{ {
return std::nullopt; if (!InitGtk())
return m_callback(DialogCallbackResultType::FAILED, std::nullopt);
auto* dialog = gtk_file_dialog_new();
// Move data out of the dialog object since it may be destroyed
auto* dialogData = new DialogData(DialogOperation::SELECT_FOLDER, std::move(m_callback));
gtk_file_dialog_select_folder(dialog, nullptr, nullptr, OnDialogResult, dialogData);
g_object_unref(dialog);
} }
} // namespace PLATFORM_NAMESPACE_LINUX #endif
} // namespace ui
#endif #endif

View File

@@ -1,70 +0,0 @@
#pragma once
#ifdef __linux__
#include "Web/Platform/Platform.h"
#include <functional>
#include <optional>
#include <string>
#include <vector>
namespace PLATFORM_NAMESPACE_LINUX
{
class FileDialogFilter
{
public:
FileDialogFilter(std::string name, std::string filter);
std::string m_name;
std::string m_filter;
};
class DialogWithCallback
{
public:
DialogWithCallback();
void SetCallback(std::function<void(std::optional<std::string> result)> callback);
protected:
std::function<void(std::optional<std::string> result)> m_callback;
};
class FileDialog : public DialogWithCallback
{
public:
FileDialog();
void AddFilter(std::string name, std::string filter);
protected:
std::vector<FileDialogFilter> m_filters;
};
class OpenFileDialog : public FileDialog
{
public:
OpenFileDialog();
void OpenAsync() const;
};
class SaveFileDialog : public FileDialog
{
public:
SaveFileDialog();
void OpenAsync() const;
};
class FolderSelectDialog : public DialogWithCallback
{
public:
FolderSelectDialog();
void OpenAsync() const;
};
} // namespace PLATFORM_NAMESPACE_LINUX
#endif

View File

@@ -1,4 +1,4 @@
#include "DialogHandlerWindows.h" #include "Web/Platform/DialogHandler.h"
#ifdef _WIN32 #ifdef _WIN32
@@ -12,7 +12,7 @@ using namespace PLATFORM_NAMESPACE_WINDOWS;
namespace namespace
{ {
bool SetFilters(IFileDialog* pFileOpen, const std::vector<FileDialogFilter>& filters) bool SetFilters(IFileDialog* pFileOpen, const std::vector<ui::FileDialogFilter>& filters)
{ {
if (filters.empty()) if (filters.empty())
return true; return true;
@@ -48,9 +48,9 @@ namespace
return SUCCEEDED(result); return SUCCEEDED(result);
} }
DialogCallbackResultType ShowFileDialog(IFileDialog* pFileDialog, std::optional<std::string>& result) ui::DialogCallbackResultType ShowFileDialog(IFileDialog* pFileDialog, std::optional<std::string>& result)
{ {
DialogCallbackResultType resultType = FAILED; auto resultType = ui::DialogCallbackResultType::FAILED;
auto hr = pFileDialog->Show(nullptr); auto hr = pFileDialog->Show(nullptr);
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
@@ -68,42 +68,22 @@ namespace
result = WideStringToString(pszFilePath); result = WideStringToString(pszFilePath);
CoTaskMemFree(pszFilePath); CoTaskMemFree(pszFilePath);
resultType = SUCCESSFUL; resultType = ui::DialogCallbackResultType::SUCCESSFUL;
} }
pItem->Release(); pItem->Release();
} }
} }
else if (HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr) else if (HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr)
{ {
resultType = CANCELLED; resultType = ui::DialogCallbackResultType::CANCELLED;
} }
return resultType; return resultType;
} }
} // namespace } // namespace
namespace PLATFORM_NAMESPACE_WINDOWS namespace ui
{ {
DialogWithCallback::DialogWithCallback() = default;
void DialogWithCallback::SetCallback(callback_t callback)
{
m_callback = std::move(callback);
}
FileDialogFilter::FileDialogFilter(std::string name, std::string filter)
: m_name(std::move(name)),
m_filter(std::move(filter))
{
}
FileDialog::FileDialog() = default;
void FileDialog::AddFilter(std::string name, std::string filter)
{
m_filters.emplace_back(std::move(name), std::move(filter));
}
OpenFileDialog::OpenFileDialog() = default; OpenFileDialog::OpenFileDialog() = default;
void OpenFileDialog::OpenAsync() void OpenFileDialog::OpenAsync()
@@ -119,11 +99,11 @@ namespace PLATFORM_NAMESPACE_WINDOWS
const auto initResult = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); const auto initResult = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (!SUCCEEDED(initResult)) if (!SUCCEEDED(initResult))
{ {
callback(FAILED, std::nullopt); callback(DialogCallbackResultType::FAILED, std::nullopt);
return; return;
} }
DialogCallbackResultType resultType = FAILED; auto resultType = DialogCallbackResultType::FAILED;
std::optional<std::string> result = std::nullopt; std::optional<std::string> result = std::nullopt;
IFileOpenDialog* pFileOpen; IFileOpenDialog* pFileOpen;
@@ -157,11 +137,11 @@ namespace PLATFORM_NAMESPACE_WINDOWS
const auto initResult = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); const auto initResult = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (!SUCCEEDED(initResult)) if (!SUCCEEDED(initResult))
{ {
callback(FAILED, std::nullopt); callback(DialogCallbackResultType::FAILED, std::nullopt);
return; return;
} }
DialogCallbackResultType resultType = FAILED; auto resultType = DialogCallbackResultType::FAILED;
std::optional<std::string> result = std::nullopt; std::optional<std::string> result = std::nullopt;
IFileSaveDialog* pFileSave; IFileSaveDialog* pFileSave;
@@ -194,7 +174,7 @@ namespace PLATFORM_NAMESPACE_WINDOWS
const auto initResult = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); const auto initResult = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (!SUCCEEDED(initResult)) if (!SUCCEEDED(initResult))
{ {
callback(FAILED, std::nullopt); callback(DialogCallbackResultType::FAILED, std::nullopt);
return; return;
} }
@@ -205,7 +185,7 @@ namespace PLATFORM_NAMESPACE_WINDOWS
if (!SUCCEEDED(hr)) if (!SUCCEEDED(hr))
{ {
CoUninitialize(); CoUninitialize();
callback(FAILED, std::nullopt); callback(DialogCallbackResultType::FAILED, std::nullopt);
return; return;
} }
@@ -214,7 +194,7 @@ namespace PLATFORM_NAMESPACE_WINDOWS
{ {
pFileOpen->Release(); pFileOpen->Release();
CoUninitialize(); CoUninitialize();
callback(FAILED, std::nullopt); callback(DialogCallbackResultType::FAILED, std::nullopt);
return; return;
} }
@@ -222,7 +202,7 @@ namespace PLATFORM_NAMESPACE_WINDOWS
{ {
pFileOpen->Release(); pFileOpen->Release();
CoUninitialize(); CoUninitialize();
callback(FAILED, std::nullopt); callback(DialogCallbackResultType::FAILED, std::nullopt);
return; return;
} }
@@ -235,5 +215,5 @@ namespace PLATFORM_NAMESPACE_WINDOWS
}) })
.detach(); .detach();
} }
} // namespace PLATFORM_NAMESPACE_WINDOWS } // namespace ui
#endif #endif

View File

@@ -1,80 +0,0 @@
#pragma once
#ifdef _WIN32
#include "Web/Platform/Platform.h"
#include <cstdint>
#include <functional>
#include <optional>
#include <string>
#include <vector>
namespace PLATFORM_NAMESPACE_WINDOWS
{
enum DialogCallbackResultType : std::uint8_t
{
SUCCESSFUL,
CANCELLED,
FAILED
};
class DialogWithCallback
{
public:
using callback_t = std::function<void(DialogCallbackResultType resultType, std::optional<std::string> result)>;
DialogWithCallback();
void SetCallback(callback_t callback);
protected:
callback_t m_callback;
};
class FileDialogFilter
{
public:
FileDialogFilter(std::string name, std::string filter);
std::string m_name;
std::string m_filter;
};
class FileDialog : public DialogWithCallback
{
public:
FileDialog();
void AddFilter(std::string name, std::string filter);
protected:
std::vector<FileDialogFilter> m_filters;
};
class OpenFileDialog : public FileDialog
{
public:
OpenFileDialog();
void OpenAsync();
};
class SaveFileDialog : public FileDialog
{
public:
SaveFileDialog();
void OpenAsync();
};
class FolderSelectDialog : public DialogWithCallback
{
public:
FolderSelectDialog();
void OpenAsync();
};
} // namespace PLATFORM_NAMESPACE_WINDOWS
#endif