feat: finish dialog helper.

- finish dialof helper.
- dialog helper still nned some annotation.
This commit is contained in:
yyc12345 2024-05-27 14:27:11 +08:00
parent a9059013f0
commit cebe2f004d
3 changed files with 299 additions and 274 deletions

View File

@ -6,6 +6,29 @@
namespace YYCC::DialogHelper {
#pragma region COM Guard
class ComGuard {
public:
ComGuard() : m_HasInit(false) {
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr)) m_HasInit = true;
}
~ComGuard() {
if (m_HasInit) {
CoUninitialize();
}
}
protected:
bool m_HasInit;
};
static const ComGuard c_ComGuard;
#pragma endregion
#pragma region FileFilters
bool FileFilters::Add(const char* filter_name, std::initializer_list<const char*> il) {
@ -16,7 +39,8 @@ namespace YYCC::DialogHelper {
// assign filter patterns
FilterModes modes;
for (const char* pattern : il) {
if (pattern != nullptr) modes.emplace_back(std::string(pattern));
if (pattern != nullptr)
modes.emplace_back(std::string(pattern));
}
// check filter patterns
@ -29,8 +53,7 @@ namespace YYCC::DialogHelper {
bool FileFilters::Generate(WinFileFilters& win_result) const {
// clear Windows oriented data
win_result.m_WinDataStruct.reset();
win_result.m_WinFilters.clear();
win_result.Clear();
// build new Windows oriented string vector first
for (const auto& it : m_Filters) {
@ -72,7 +95,57 @@ namespace YYCC::DialogHelper {
#pragma region File Dialog
bool FileDialog::Generate(WinFileDialog& win_result) const {
return false;
// clear Windows oriented data
win_result.Clear();
// set owner
win_result.m_WinOwner = m_Owner;
// build file filters
if (!m_FileTypes.Generate(win_result.m_WinFileTypes))
return false;
// check default file type index
// check value overflow (comparing with >= because we need plus 1 for file type index later)
if (m_DefaultFileTypeIndex >= std::numeric_limits<UINT>::max())
return false;
// check invalid index (overflow the length or registered file types if there is file type)
if (m_FileTypes.Count() != 0u && m_DefaultFileTypeIndex >= m_FileTypes.Count())
return false;
// set index with additional plus according to Windows specification.
win_result.m_WinDefaultFileTypeIndex = static_cast<UINT>(m_DefaultFileTypeIndex + 1);
// build title and init file name
if (m_HasTitle) {
if (!YYCC::EncodingHelper::UTF8ToWchar(m_Title.c_str(), win_result.m_WinTitle))
return false;
win_result.m_HasTitle = true;
}
if (m_HasInitFileName) {
if (!YYCC::EncodingHelper::UTF8ToWchar(m_InitFileName.c_str(), win_result.m_WinInitFileName))
return false;
win_result.m_HasInitFileName = true;
}
// fetch init directory
if (m_HasInitDirectory) {
// convert to wpath
std::wstring w_init_directory;
if (!YYCC::EncodingHelper::UTF8ToWchar(m_InitDirectory.c_str(), w_init_directory))
return false;
// fetch IShellItem*
// Ref: https://stackoverflow.com/questions/76306324/how-to-set-default-folder-for-ifileopendialog-interface
IShellItem* init_directory = NULL;
HRESULT hr = SHCreateItemFromParsingName(w_init_directory.c_str(), NULL, IID_PPV_ARGS(&init_directory));
if (FAILED(hr)) return false;
// assign IShellItem*
win_result.m_WinInitDirectory.reset(init_directory);
}
// everything is okey
return true;
}
#pragma endregion
@ -86,7 +159,20 @@ namespace YYCC::DialogHelper {
OpenFolder
};
using SmartFileDialogPtr = std::unique_ptr<IFileDialog, std::function<void(IFileDialog*)>>;
bool ExtractDisplayName(IShellItem* item, std::string& ret) {
// fetch display name from IShellItem*
WCHAR* _name;
HRESULT hr = item->GetDisplayName(SIGDN_FILESYSPATH, &_name);
if (FAILED(hr)) return false;
SmartLPWSTR display_name(_name);
// convert result
if (!YYCC::EncodingHelper::WcharToUTF8(display_name.get(), ret))
return false;
// finished
return true;
}
template<CommonFileDialogType EDialogType>
bool CommonFileDialog(const FileDialog& params, std::vector<std::string>& ret) {
@ -117,22 +203,16 @@ namespace YYCC::DialogHelper {
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&_pfd)
);
if (!SUCCEEDED(hr)) return false;
if (FAILED(hr)) return false;
// create memory-safe dialog pointer
SmartFileDialogPtr pfd(
_pfd,
[](IFileDialog* instance) -> void {
if (instance != nullptr)
instance->Release();
}
);
SmartIFileDialog pfd(_pfd);
// set options for dialog
// before setting, always get the options first in order.
// not to override existing options.
DWORD dwFlags;
hr = pfd->GetOptions(&dwFlags);
if (!SUCCEEDED(hr)) return false;
if (FAILED(hr)) return false;
// modify options
switch (EDialogType) {
// We want user only can pick file system files: FOS_FORCEFILESYSTEM.
@ -162,98 +242,109 @@ namespace YYCC::DialogHelper {
}
// set folder dialog options
hr = pfd->SetOptions(dwFlags);
if (!SUCCEEDED(hr)) return false;
if (FAILED(hr)) return false;
// set title
std::wstring wtitle, winit_filename, winit_directory;
if (params.GetTitle() != nullptr) {
EncodingHelper::UTF8ToWchar(params.GetTitle(), wtitle);
// build Windows used file dialog parameters
WinFileDialog win_params;
if (!params.Generate(win_params))
return false;
// setup title and init file name
if (win_params.HasTitle()) {
hr = pfd->SetTitle(win_params.GetTitle());
if (FAILED(hr)) return false;
}
if (win_params.HasInitFileName()) {
hr = pfd->SetFileName(win_params.GetInitFileName());
if (FAILED(hr)) return false;
}
// setup init directory
if (win_params.HasInitDirectory()) {
hr = pfd->SetFolder(win_params.GetInitDirectory());
}
// set file types and default file index when we picking file
WinFileFilters win_file_filters;
if constexpr (EDialogType != CommonFileDialogType::OpenFolder) {
// generate data from user specified file filters
const auto& file_filters = params.GetFileTypes();
if (!file_filters.Generate(win_file_filters))
return false;
// set file types list
hr = pfd->SetFileTypes(win_file_filters.GetFilterCount(), win_file_filters.GetFilterSpecs());
if (!SUCCEEDED(hr)) return false;
const auto& file_filters = win_params.GetFileTypes();
hr = pfd->SetFileTypes(file_filters.GetFilterCount(), file_filters.GetFilterSpecs());
if (FAILED(hr)) return false;
// set default file type index
// Windows order this is 1-based index.
// We plus 1 for it because we used is 0-based index.
hr = pfd->SetFileTypeIndex(params.GetDefaultFileTypeIndex() + 1);
if (!SUCCEEDED(hr)) return false;
hr = pfd->SetFileTypeIndex(win_params.GetDefaultFileTypeIndex());
if (FAILED(hr)) return false;
}
// CoCreate the File Open Dialog object.
IFileDialog* pfd = NULL;
HRESULT hr = CoCreateInstance(
CLSID_FileOpenDialog,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pfd)
);
if (SUCCEEDED(hr)) {
// Set the options on the dialog.
DWORD dwFlags;
// show the dialog
hr = pfd->Show(win_params.HasOwner() ? win_params.GetOwner() : nullptr);
if (FAILED(hr)) return false;
// Before setting, always get the options first in order
// not to override existing options.
hr = pfd->GetOptions(&dwFlags);
if (SUCCEEDED(hr)) {
// In this case, get shell items only for file system items.
hr = pfd->SetOptions(dwFlags | FOS_FORCEFILESYSTEM);
if (SUCCEEDED(hr)) {
// Set the file types to display only.
// Notice that this is a 1-based array.
hr = pfd->SetFileTypes(ARRAYSIZE(c_rgSaveTypes), c_rgSaveTypes);
if (SUCCEEDED(hr)) {
// Set the selected file type index to Word Docs for this example.
hr = pfd->SetFileTypeIndex(INDEX_WORDDOC);
if (SUCCEEDED(hr)) {
// Set the default extension to be ".doc" file.
hr = pfd->SetDefaultExtension(L"doc;docx");
if (SUCCEEDED(hr)) {
// Show the dialog
hr = pfd->Show(NULL);
if (SUCCEEDED(hr)) {
// Obtain the result once the user clicks
// the 'Open' button.
// The result is an IShellItem object.
IShellItem* psiResult;
hr = pfd->GetResult(&psiResult);
if (SUCCEEDED(hr)) {
// We are just going to print out the
// name of the file for sample sake.
PWSTR pszFilePath = NULL;
hr = psiResult->GetDisplayName(SIGDN_FILESYSPATH,
&pszFilePath);
if (SUCCEEDED(hr)) {
TaskDialog(NULL,
NULL,
L"CommonFileDialogApp",
pszFilePath,
NULL,
TDCBF_OK_BUTTON,
TD_INFORMATION_ICON,
NULL);
CoTaskMemFree(pszFilePath);
}
psiResult->Release();
}
}
}
}
}
// obtain result when user click "OK" button.
switch (EDialogType) {
case CommonFileDialogType::OpenFile:
case CommonFileDialogType::OpenFolder:
case CommonFileDialogType::SaveFile:
{
// obtain one file entry
IShellItem* _item;
hr = pfd->GetResult(&_item);
if (FAILED(hr)) return false;
SmartIShellItem result_item(_item);
// extract display name
std::string result_name;
if (!ExtractDisplayName(result_item.get(), result_name))
return false;
// append result
ret.emplace_back(std::move(result_name));
}
break;
case CommonFileDialogType::OpenMultipleFiles:
{
// try casting file dialog to file open dialog
// Ref: https://learn.microsoft.com/en-us/windows/win32/learnwin32/asking-an-object-for-an-interface
IFileOpenDialog* _pfod = nullptr;
hr = pfd->QueryInterface(IID_PPV_ARGS(&_pfod));
if (FAILED(hr)) return false;
SmartIFileOpenDialog pfod(_pfod);
// obtain multiple file entires
IShellItemArray* _items;
hr = pfod->GetResults(&_items);
if (FAILED(hr)) return false;
SmartIShellItemArray result_items(_items);
// analyze file entries
// get array count first
DWORD result_items_count = 0u;
hr = result_items->GetCount(&result_items_count);
if (FAILED(hr)) return false;
// iterate array
for (DWORD i = 0u; i < result_items_count; ++i) {
// fetch item by index
IShellItem* _item;;
hr = result_items->GetItemAt(i, &_item);
if (FAILED(hr)) return false;
SmartIShellItem result_item(_item);
// extract display name
std::string result_name;
if (!ExtractDisplayName(result_item.get(), result_name))
return false;
// append result
ret.emplace_back(std::move(result_name));
}
}
pfd->Release();
break;
default:
return false;
}
return SUCCEEDED(hr);
// everything is okey
return true;
}
#pragma endregion
@ -285,159 +376,6 @@ namespace YYCC::DialogHelper {
#pragma endregion
//template<bool TIsOpen, bool TMultiSelection>
//bool GeneralFileDialog(const FileDialogParameter& params, std::vector<std::string>& ret) {
// // make sure multi-selection only available in open mode
// static_assert(TIsOpen && (!TIsOpen && TMultiSelection == false));
// // build filter
// std::wstring w_filter;
// for (const auto& filter_pair : params.m_Filter) {
// w_filter += EncodingHelper::UTF8ToWchar(filter_pair.first.c_str());
// w_filter += L'\0';
// w_filter += EncodingHelper::UTF8ToWchar(filter_pair.second.c_str());
// w_filter += L'\0';
// }
// // build title
// std::wstring w_title(EncodingHelper::UTF8ToWchar(params.m_Title.c_str()));
// // build default extension
// std::wstring w_default_ext(EncodingHelper::UTF8ToWchar(params.m_DefaultExtension.c_str()));
// // build initial directory
// std::wstring w_init_dir(EncodingHelper::UTF8ToWchar(params.m_InitialDirectory.c_str()));
// // prepare file name receiver and preset it as initial file name
// std::wstring path_receiver(EncodingHelper::UTF8ToWchar(params.m_InitialFileName.c_str()));
// path_receiver.resize(std::max(MAX_PATH, path_receiver.size()), L'\0');
// // prepare the common part of file dialog struct
// OPENFILENAMEW dialog_param;
// ZeroMemory(&dialog_param, sizeof(OPENFILENAMEW));
// dialog_param.lStructSize = sizeof(OPENFILENAMEW);
// // dialog owner
// dialog_param.hwndOwner = params.m_Owner;
// // if no filter, we pass NULL
// dialog_param.lpstrFilter = w_filter.empty() ? NULL : w_filter.c_str();
// // no record to user selected filter
// dialog_param.lpstrCustomFilter = NULL;
// dialog_param.nMaxCustFilter = 0;
// dialog_param.nFilterIndex = 0;
// // path receiver, also is init filename
// dialog_param.lpstrFile = path_receiver.data();
// dialog_param.nMaxFile = static_caast<DWORD>(path_receiver.size());
// // no selected file infos
// dialog_param.lpstrFileTitle = NULL;
// dialog_param.nMaxFileTitle = 0;
// // initial directory
// dialog_param.lpstrInitialDir = w_init_dir.empty() ? NULL : w_init_dir.c_str();
// // dialog title
// dialog_param.lpstrTitle = w_title.empty() ? NULL : w_title.c_str();
// // setup basic flags
// dialog_param.Flags = OFN_EXPLORER;
// // default extension
// dialog_param.lpstrDefExt = w_default_ext.empty() ? NULL : w_default_ext.c_str();
// BOOL status;
// if constexpr (TIsOpen) {
// // multi-selection need add special multi-selection flags
// if constexpr (TMultiSelection) {
// dialog_param.Flags |= OFN_ALLOWMULTISELECT;
// }
// // call browser
// status = GetOpenFileNameW(&dialog_param);
// // only process result when success
// if (status) {
// if constexpr (TMultiSelection) {
// // get directory part, copy from start to dialog param specified offset
// std::wstring w_directory_part(path_receiver.c_str(), dialog_param.nFileOffset);
// // get file names part one by one
// size_t filename_cursor = dialog_param.nFileOffset;
// while (path_receiver[filename_cursor] != L'\0') {
// // init wstring from given offset
// std::wstring w_filename_part(path_receiver.c_str() + filename_cursor);
// // get eaten chars from result and increase to cursor
// filename_cursor += w_filename_part.size() + 1u;
// // combine 2 parts and insert into list
// ret.emplace_back(std::string(EncodingHelper::WcharToUTF8(w_directory_part + w_filename_part)));
// }
// } else {
// ret.emplace_back(std::string(EncodingHelper::WcharToUTF8(path_receiver.c_str())));
// }
// }
// } else {
// // call browser
// status = GetSaveFileNameW(&dialog_param);
// // only process result when success
// if (status) {
// ret.emplace_back(std::string(EncodingHelper::WcharToUTF8(path_receiver.c_str())));
// }
// }
// // if failed, clear result
// // and return result
// if (!status) {
// ret.clear();
// }
// return status == TRUE;
//}
//bool OpenFileDialog(const FileDialogParameter& params, std::string& ret) {
// std::vector<std::string> cache;
// bool isok = GeneralFileDialog<true, false>(params, cache);
// if (isok) ret = cache.front();
// return isok;
//}
//bool OpenMultipleFileDialog(const FileDialogParameter& params, std::vector<std::string>& ret) {
// return GeneralFileDialog<true, true>(params, ret);
//}
//bool SaveFileDialog(const FileDialogParameter& params, std::string& ret) {
// std::vector<std::string> cache;
// bool isok = GeneralFileDialog<false, false>(params, cache);
// if (isok) ret = cache.front();
// return isok;
//}
//bool OpenFolderDialog(const FolderDialogParameter& params, std::string& ret) {
// // create wchar string cache for windows W-tail function
// std::wstring w_title(EncodingHelper::UTF8ToWchar(params.m_Title.c_str()));
// // create buffer for receiving selected folder
// // initialize with MAX_PATH length and content is filled with zero.
// std::wstring path_receiver(MAX_PATH, L'\0');
// // prepare folder foalog struct
// BROWSEINFOW dialog_param = { 0 };
// dialog_param.hwndOwner = params.m_Owner;
// dialog_param.pidlRoot = nullptr;
// dialog_param.pszDisplayName = path_receiver.data();
// dialog_param.lpszTitle = w_title.c_str();
// dialog_param.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI;
// dialog_param.lpfn = nullptr;
// // call browser
// PIDLIST_ABSOLUTE place = SHBrowseForFolderW(&dialog_param);
// // if browser failed, return.
// if (place == nullptr) {
// ret.clear();
// return false;
// }
// // get path from browser result
// BOOL status;
// if (status = SHGetPathFromIDListW(place, path_receiver.data())) {
// EncodingHelper::WcharToUTF8(path_receiver.c_str(), ret);
// } else {
// ret.clear();
// }
// // clear browser result and return
// CoTaskMemFree(place);
// return status == TRUE;
//}
}
#endif

View File

@ -10,17 +10,50 @@
#include "WinImportPrefix.hpp"
#include <Windows.h>
#include <shlobj_core.h>
#include <commdlg.h>
#include "WinImportSuffix.hpp"
namespace YYCC::DialogHelper {
#pragma region COM Pointer Management
class ComPtrDeleter {
public:
ComPtrDeleter() {}
void operator() (IUnknown* com_ptr) {
if (com_ptr != nullptr) {
com_ptr->Release();
}
}
};
using SmartIFileDialog = std::unique_ptr<IFileDialog, ComPtrDeleter>;
using SmartIFileOpenDialog = std::unique_ptr<IFileOpenDialog, ComPtrDeleter>;
using SmartIShellItem = std::unique_ptr<IShellItem, ComPtrDeleter>;
using SmartIShellItemArray = std::unique_ptr<IShellItemArray, ComPtrDeleter>;
using SmartIShellFolder = std::unique_ptr<IShellFolder, ComPtrDeleter>;
template<typename _Ty>
class CoTaskMemDeleter {
public:
CoTaskMemDeleter() {}
void operator() (_Ty* com_ptr) {
if (com_ptr != nullptr) {
CoTaskMemFree(com_ptr);
}
}
};
using SmartLPWSTR = std::unique_ptr<WCHAR, CoTaskMemDeleter<WCHAR>>;
#pragma endregion
/**
* @brief The class represent the file types region in file dialog
* @details THis class is specific for Windows use, not user oriented.
*/
class WinFileFilters {
friend class FileFilters;
friend class WinFileDialog;
public:
WinFileFilters() : m_WinFilters(), m_WinDataStruct(nullptr) {}
@ -38,12 +71,17 @@ namespace YYCC::DialogHelper {
std::vector<WinFilterPair> m_WinFilters;
std::unique_ptr<COMDLG_FILTERSPEC[]> m_WinDataStruct;
void Clear() {
m_WinDataStruct.reset();
m_WinFilters.clear();
}
};
/**
* @brief The class represent the file types region in file dialog.
* @details THis class is user oriented. User can use function manipulate file types
* and final fialog function will produce Windows-understood data struct from this.
* @details This class is user oriented. User can use function manipulate file types
* and final generation function will produce Windows-understood data struct from this.
*/
class FileFilters {
public:
@ -86,46 +124,69 @@ namespace YYCC::DialogHelper {
std::vector<FilterPair> m_Filters;
};
/**
* @brief The class represent the file dialog
* @details THis class is specific for Windows use, not user oriented.
*/
class WinFileDialog {
friend class FileDialog;
public:
WinFileDialog() :
m_WinOwner(NULL),
m_WinFileTypes(), m_WinDefaultFileTypeIndex(0u),
m_HasTitle(false), m_HasInitFileName(false), m_WinTitle(), m_WinInitFileName(),
m_WinInitDirectory(nullptr)
{}
m_HasTitle(false), m_HasInitFileName(false), m_WinTitle(), m_WinInitFileName(),
m_WinInitDirectory(nullptr) {}
bool HasOwner() const { return m_WinOwner != NULL; }
HWND GetOwner() const { return m_WinOwner; }
bool HasTitle() const { return !m_WinTitle.empty(); }
const WinFileFilters& GetFileTypes() const { return m_WinFileTypes; }
UINT GetDefaultFileTypeIndex() const { return m_WinDefaultFileTypeIndex; }
bool HasTitle() const { return m_HasTitle; }
const wchar_t* GetTitle() const { return m_WinTitle.c_str(); }
bool HasInitFileName() const { return !m_WinInitFileName.empty(); }
bool HasInitFileName() const { return m_HasInitFileName; }
const wchar_t* GetInitFileName() const { return m_WinInitFileName.c_str(); }
bool HasInitDirectory() const { return m_WinInitDirectory != nullptr;}
const IShellItem* GetInitDirectory() const { return m_WinInitDirectory.get(); }
bool HasInitDirectory() const { return m_WinInitDirectory.get() != nullptr; }
IShellItem* GetInitDirectory() const { return m_WinInitDirectory.get(); }
protected:
HWND m_WinOwner;
WinFileFilters m_WinFileTypes;
/**
* @brief The default selected file type in dialog
* @remarks This is 1-based index according to Windows specification.
*/
UINT m_WinDefaultFileTypeIndex;
bool m_HasTitle, m_HasInitFileName;
std::wstring m_WinTitle, m_WinInitFileName;
SmartIShellItem m_WinInitDirectory;
using SmartShellItem = std::unique_ptr<IShellItem, std::function<void(IShellItem*)>>;
SmartShellItem m_WinInitDirectory;
void Clear() {
m_WinOwner = nullptr;
m_WinFileTypes.Clear();
m_WinDefaultFileTypeIndex = 0u;
m_HasTitle = m_HasInitFileName = false;
m_WinTitle.clear();
m_WinInitFileName.clear();
m_WinInitDirectory.reset();
}
};
/**
* @brief The class represent the file dialog.
* @details This class is user oriented. User can use function manipulate file dialog properties
* and final generation function will produce Windows-understood data struct from this.
*/
class FileDialog {
public:
FileDialog() :
m_Owner(NULL), m_Title(),
m_Owner(NULL),
m_FileTypes(),
m_DefaultFileTypeIndex(0u),
m_InitFileName(), m_InitDirectory() {}
m_Title(), m_InitFileName(), m_InitDirectory(),
m_HasTitle(false), m_HasInitFileName(false), m_HasInitDirectory(false) {}
void SetOwner(HWND owner) { m_Owner = owner; }
void SetTitle(const char* title) {
@ -135,7 +196,7 @@ namespace YYCC::DialogHelper {
FileFilters& ConfigreFileTypes() {
return m_FileTypes;
}
void SetDefaultFileTypeIndex(UINT idx) { m_DefaultFileTypeIndex = idx; }
void SetDefaultFileTypeIndex(size_t idx) { m_DefaultFileTypeIndex = idx; }
void SetInitFileName(const char* init_filename) {
if (m_HasInitFileName = init_filename != nullptr)
m_InitFileName = init_filename;
@ -145,21 +206,29 @@ namespace YYCC::DialogHelper {
m_InitDirectory = init_dir;
}
void Clear() {
m_Owner = nullptr;
m_HasTitle = m_HasInitFileName = m_HasInitDirectory = false;
m_Title.clear();
m_InitFileName.clear();
m_InitDirectory.clear();
m_FileTypes.Clear();
m_DefaultFileTypeIndex = 0u;
}
bool Generate(WinFileDialog& win_result) const;
protected:
HWND m_Owner;
bool m_HasTitle, m_HasInitFileName, m_HasInitDirectory;
std::string m_Title, m_InitFileName, m_InitDirectory;
FileFilters m_FileTypes;
/**
* @brief The default file type selected in dialog
* @brief The default selected file type in dialog
* @remarks Although Windows notice that this is a 1-based index,
* but for universal experience, we order this is 0-based index.
*/
UINT m_DefaultFileTypeIndex;
size_t m_DefaultFileTypeIndex;
};
bool OpenFileDialog(const FileDialog& params, std::string& ret);

View File

@ -53,15 +53,33 @@ namespace Testbench {
}
static void DialogTestbench() {
YYCC::DialogHelper::FileFilters test;
test.Add("Microsoft Word (*.docx; *.doc)", {"*.docx", "*.doc"});
test.Add("Microsoft Excel (*.xlsx; *.xls)", {"*.xlsx", "*.xls"});
test.Add("Microsoft PowerPoint (*.pptx; *.ppt)", {"*.pptx", "*.ppt"});
test.Add("Text File (*.*)", {"*.txt"});
test.Add("All Files (*.*)", {"*.*"});
std::string ret;
std::vector<std::string> rets;
YYCC::DialogHelper::WinFileFilters win_file_filters;
bool ret = test.Generate(win_file_filters);
YYCC::DialogHelper::FileDialog params;
auto& filters = params.ConfigreFileTypes();
filters.Add("Microsoft Word (*.docx; *.doc)", {"*.docx", "*.doc"});
filters.Add("Microsoft Excel (*.xlsx; *.xls)", {"*.xlsx", "*.xls"});
filters.Add("Microsoft PowerPoint (*.pptx; *.ppt)", {"*.pptx", "*.ppt"});
filters.Add("Text File (*.txt)", {"*.txt"});
filters.Add("All Files (*.*)", {"*.*"});
params.SetDefaultFileTypeIndex(0u);
if (YYCC::DialogHelper::OpenFileDialog(params, ret)) {
YYCC::TerminalHelper::FPrintf(stdout, u8"Open File: %s\n", ret.c_str());
}
if (YYCC::DialogHelper::OpenMultipleFileDialog(params, rets)) {
YYCC::TerminalHelper::FPuts(u8"Open Multiple Files:\n", stdout);
for (const auto& item : rets) {
YYCC::TerminalHelper::FPrintf(stdout, u8"\t%s\n", item.c_str());
}
}
if (YYCC::DialogHelper::SaveFileDialog(params, ret)) {
YYCC::TerminalHelper::FPrintf(stdout, u8"Save File: %s\n", ret.c_str());
}
params.Clear();
if (YYCC::DialogHelper::OpenFolderDialog(params, ret)) {
YYCC::TerminalHelper::FPrintf(stdout, u8"Open Folder: %s\n", ret.c_str());
}
}
}