fix: fix all build issue of dialog namespace but not test.
This commit is contained in:
@ -300,6 +300,9 @@ StatementMacros:
|
|||||||
- YYCC_DEFAULT_COPY
|
- YYCC_DEFAULT_COPY
|
||||||
- YYCC_DEFAULT_MOVE
|
- YYCC_DEFAULT_MOVE
|
||||||
- YYCC_DEFAULT_COPY_MOVE
|
- YYCC_DEFAULT_COPY_MOVE
|
||||||
|
- YYCC_DECL_COPY
|
||||||
|
- YYCC_DECL_MOVE
|
||||||
|
- YYCC_DECL_COPY_MOVE
|
||||||
TableGenBreakInsideDAGArg: DontBreak
|
TableGenBreakInsideDAGArg: DontBreak
|
||||||
TabWidth: 4
|
TabWidth: 4
|
||||||
UseTab: Never
|
UseTab: Never
|
||||||
|
@ -17,7 +17,7 @@ PRIVATE
|
|||||||
yycc/rust/panic.cpp
|
yycc/rust/panic.cpp
|
||||||
yycc/rust/env.cpp
|
yycc/rust/env.cpp
|
||||||
yycc/windows/com.cpp
|
yycc/windows/com.cpp
|
||||||
#yycc/windows/dialog.cpp
|
yycc/windows/dialog.cpp
|
||||||
yycc/windows/winfct.cpp
|
yycc/windows/winfct.cpp
|
||||||
yycc/encoding/stl.cpp
|
yycc/encoding/stl.cpp
|
||||||
yycc/encoding/windows.cpp
|
yycc/encoding/windows.cpp
|
||||||
@ -61,7 +61,7 @@ FILES
|
|||||||
yycc/windows/import_guard_head.hpp
|
yycc/windows/import_guard_head.hpp
|
||||||
yycc/windows/import_guard_tail.hpp
|
yycc/windows/import_guard_tail.hpp
|
||||||
yycc/windows/com.hpp
|
yycc/windows/com.hpp
|
||||||
#yycc/windows/dialog.hpp
|
yycc/windows/dialog.hpp
|
||||||
yycc/windows/winfct.hpp
|
yycc/windows/winfct.hpp
|
||||||
yycc/constraint.hpp
|
yycc/constraint.hpp
|
||||||
yycc/constraint/builder.hpp
|
yycc/constraint/builder.hpp
|
||||||
|
@ -7,8 +7,8 @@
|
|||||||
|
|
||||||
/// @brief Explicitly remove move (\c constructor and \c operator\=) for given class.
|
/// @brief Explicitly remove move (\c constructor and \c operator\=) for given class.
|
||||||
#define YYCC_DELETE_MOVE(CLSNAME) \
|
#define YYCC_DELETE_MOVE(CLSNAME) \
|
||||||
CLSNAME(CLSNAME&&) = delete; \
|
CLSNAME(CLSNAME&&) noexcept = delete; \
|
||||||
CLSNAME& operator=(CLSNAME&&) = delete;
|
CLSNAME& operator=(CLSNAME&&) noexcept = delete;
|
||||||
|
|
||||||
/// @brief Explicitly remove (copy and move) (\c constructor and \c operator\=) for given class.
|
/// @brief Explicitly remove (copy and move) (\c constructor and \c operator\=) for given class.
|
||||||
#define YYCC_DELETE_COPY_MOVE(CLSNAME) \
|
#define YYCC_DELETE_COPY_MOVE(CLSNAME) \
|
||||||
@ -22,10 +22,41 @@
|
|||||||
|
|
||||||
/// @brief Explicitly set default move (\c constructor and \c operator\=) for given class.
|
/// @brief Explicitly set default move (\c constructor and \c operator\=) for given class.
|
||||||
#define YYCC_DEFAULT_MOVE(CLSNAME) \
|
#define YYCC_DEFAULT_MOVE(CLSNAME) \
|
||||||
CLSNAME(CLSNAME&&) = default; \
|
CLSNAME(CLSNAME&&) noexcept = default; \
|
||||||
CLSNAME& operator=(CLSNAME&&) = default;
|
CLSNAME& operator=(CLSNAME&&) noexcept = default;
|
||||||
|
|
||||||
/// @brief Explicitly set default (copy and move) (\c constructor and \c operator\=) for given class.
|
/// @brief Explicitly set default (copy and move) (\c constructor and \c operator\=) for given class.
|
||||||
#define YYCC_DEFAULT_COPY_MOVE(CLSNAME) \
|
#define YYCC_DEFAULT_COPY_MOVE(CLSNAME) \
|
||||||
YYCC_DEFAULT_COPY(CLSNAME) \
|
YYCC_DEFAULT_COPY(CLSNAME) \
|
||||||
YYCC_DEFAULT_MOVE(CLSNAME)
|
YYCC_DEFAULT_MOVE(CLSNAME)
|
||||||
|
|
||||||
|
/// @brief Make declaration of copy (\c constructor and \c operator\=) for given class to avoid typo.
|
||||||
|
#define YYCC_DECL_COPY(CLSNAME) \
|
||||||
|
CLSNAME(const CLSNAME&); \
|
||||||
|
CLSNAME& operator=(const CLSNAME&);
|
||||||
|
|
||||||
|
/// @brief Make declaration of move (\c constructor and \c operator\=) for given class to avoid typo.
|
||||||
|
#define YYCC_DECL_MOVE(CLSNAME) \
|
||||||
|
CLSNAME(CLSNAME&&) noexcept; \
|
||||||
|
CLSNAME& operator=(CLSNAME&&) noexcept;
|
||||||
|
|
||||||
|
/// @brief Make declaration of copy and move (\c constructor and \c operator\=) for given class to avoid typo.
|
||||||
|
#define YYCC_DECL_COPY_MOVE(CLSNAME) \
|
||||||
|
YYCC_DECL_COPY(CLSNAME) \
|
||||||
|
YYCC_DECL_MOVE(CLSNAME)
|
||||||
|
|
||||||
|
/// @brief Make implementation signature of copy \c constrctor for given class and right operand name to avoid typo.
|
||||||
|
#define YYCC_IMPL_COPY_CTOR(CLSNAME, RHS) \
|
||||||
|
CLSNAME::CLSNAME(const CLSNAME& RHS)
|
||||||
|
|
||||||
|
/// @brief Make implementation signature of copy \c operator\= for given class and right operand name to avoid typo.
|
||||||
|
#define YYCC_IMPL_COPY_OPER(CLSNAME, RHS) \
|
||||||
|
CLSNAME& CLSNAME::operator=(const CLSNAME& RHS)
|
||||||
|
|
||||||
|
/// @brief Make implementation signature of move \c constrctor for given class and right operand name to avoid typo.
|
||||||
|
#define YYCC_IMPL_MOVE_CTOR(CLSNAME, RHS) \
|
||||||
|
CLSNAME::CLSNAME(CLSNAME&& RHS) noexcept
|
||||||
|
|
||||||
|
/// @brief Make implementation signature of move \c operator\= for given class and right operand name to avoid typo.
|
||||||
|
#define YYCC_IMPL_MOVE_OPER(CLSNAME, RHS) \
|
||||||
|
CLSNAME& CLSNAME::operator=(CLSNAME&& RHS) noexcept
|
||||||
|
@ -1,344 +1,606 @@
|
|||||||
#include "dialog.hpp"
|
#include "dialog.hpp"
|
||||||
#if defined(YYCC_OS_WINDOWS) && defined(YYCC_STL_MSSTL)
|
#if defined(YYCC_OS_WINDOWS) && defined(YYCC_STL_MSSTL)
|
||||||
|
|
||||||
|
#include "../string/op.hpp"
|
||||||
|
#include "../encoding/windows.hpp"
|
||||||
|
#include "../num/safe_cast.hpp"
|
||||||
|
#include "../num/safe_op.hpp"
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#define ENC ::yycc::encoding::windows
|
||||||
|
#define OP ::yycc::string::op
|
||||||
|
#define SAFECAST ::yycc::num::safe_cast
|
||||||
|
#define SAFEOP ::yycc::num::safe_op
|
||||||
|
#define WINCOM ::yycc::windows::com
|
||||||
|
|
||||||
namespace yycc::windows::dialog {
|
namespace yycc::windows::dialog {
|
||||||
|
|
||||||
#pragma region FileFilters
|
#pragma region WinFileFilters
|
||||||
|
|
||||||
bool FileFilters::Add(const yycc_char8_t* filter_name, std::initializer_list<const yycc_char8_t*> il) {
|
WinFileFilters::WinFileFilters() : m_WinFilters(), m_WinDataStruct() {}
|
||||||
// assign filter name
|
|
||||||
if (filter_name == nullptr) return false;
|
|
||||||
FilterName name(filter_name);
|
|
||||||
|
|
||||||
// assign filter patterns
|
WinFileFilters::~WinFileFilters() {}
|
||||||
FilterModes modes;
|
|
||||||
for (const yycc_char8_t* pattern : il) {
|
YYCC_IMPL_COPY_CTOR(WinFileFilters, rhs) : m_WinFilters(rhs.m_WinFilters), m_WinDataStruct() {
|
||||||
if (pattern != nullptr) modes.emplace_back(yycc_u8string(pattern));
|
// Update data
|
||||||
|
this->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
// check filter patterns
|
YYCC_IMPL_COPY_OPER(WinFileFilters, rhs) {
|
||||||
if (modes.empty()) return false;
|
// Copy data and mark desync
|
||||||
|
this->m_WinFilters = rhs.m_WinFilters;
|
||||||
// add into pairs and return
|
this->m_WinDataStruct.clear();
|
||||||
m_Filters.emplace_back(std::make_pair(std::move(name), std::move(modes)));
|
// Update data
|
||||||
return true;
|
this->update();
|
||||||
|
// Return self
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileFilters::Generate(WinFileFilters& win_result) const {
|
YYCC_IMPL_MOVE_CTOR(WinFileFilters, rhs) : m_WinFilters(std::move(rhs.m_WinFilters)), m_WinDataStruct() {
|
||||||
// clear Windows oriented data
|
// In theory, there is no update should perform,
|
||||||
win_result.Clear();
|
// however we do it because there is no guarantee that no memory allocation during this move.
|
||||||
|
this->update();
|
||||||
// build new Windows oriented string vector first
|
|
||||||
for (const auto& it : m_Filters) {
|
|
||||||
// convert name to wchar
|
|
||||||
WinFileFilters::WinFilterName name;
|
|
||||||
if (!YYCC::EncodingHelper::UTF8ToWchar(it.first, name)) return false;
|
|
||||||
|
|
||||||
// convert pattern and join them
|
|
||||||
const auto& filter_modes = it.second;
|
|
||||||
yycc_u8string joined_modes(YYCC::StringHelper::Join(filter_modes.begin(), filter_modes.end(), YYCC_U8(";")));
|
|
||||||
WinFileFilters::WinFilterModes modes;
|
|
||||||
if (!YYCC::EncodingHelper::UTF8ToWchar(joined_modes, modes)) return false;
|
|
||||||
|
|
||||||
// append new pair
|
|
||||||
win_result.m_WinFilters.emplace_back(std::make_pair(name, modes));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check filter size
|
YYCC_IMPL_MOVE_OPER(WinFileFilters, rhs) {
|
||||||
// if it overflow the maximum value, return false
|
// Move data
|
||||||
size_t count = win_result.m_WinFilters.size();
|
this->m_WinFilters = std::move(rhs.m_WinFilters);
|
||||||
if (count > std::numeric_limits<UINT>::max()) return false;
|
this->m_WinDataStruct.clear();
|
||||||
|
// Same reason for updating
|
||||||
// create new win data struct
|
this->update();
|
||||||
// and assign string pointer from internal built win string vector.
|
// Return self
|
||||||
win_result.m_WinDataStruct.reset(new COMDLG_FILTERSPEC[count]);
|
return *this;
|
||||||
for (size_t i = 0u; i < count; ++i) {
|
|
||||||
win_result.m_WinDataStruct[i].pszName = win_result.m_WinFilters[i].first.c_str();
|
|
||||||
win_result.m_WinDataStruct[i].pszSpec = win_result.m_WinFilters[i].second.c_str();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// everything is okey
|
UINT WinFileFilters::get_filter_count() const {
|
||||||
return true;
|
// We have check this length when building this class,
|
||||||
|
// so we can safely and directly cast it in there.
|
||||||
|
return static_cast<UINT>(m_WinFilters.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
const COMDLG_FILTERSPEC* WinFileFilters::get_filter_specs() const {
|
||||||
|
return m_WinDataStruct.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WinFileFilters::update() {
|
||||||
|
// Make sure they have same size
|
||||||
|
auto n = m_WinFilters.size();
|
||||||
|
m_WinDataStruct.resize(n);
|
||||||
|
|
||||||
|
// Assign data one by one
|
||||||
|
for (auto i = 0; i < n; ++i) {
|
||||||
|
// Fetch item
|
||||||
|
const auto& filter = m_WinFilters[n];
|
||||||
|
auto& data_struct = m_WinDataStruct[n];
|
||||||
|
// Assign pointer
|
||||||
|
data_struct.pszName = filter.first.c_str();
|
||||||
|
data_struct.pszSpec = filter.second.c_str();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region File Dialog
|
#pragma region FileFilters
|
||||||
|
|
||||||
bool FileDialog::Generate(WinFileDialog& win_result) const {
|
FileFilters::FileFilters() : m_Filters() {}
|
||||||
// clear Windows oriented data
|
|
||||||
win_result.Clear();
|
FileFilters::~FileFilters() {}
|
||||||
|
|
||||||
|
void FileFilters::add_filter(const std::u8string_view& filter_name, std::initializer_list<std::u8string_view> il) {
|
||||||
|
// assign filter name
|
||||||
|
FilterName name(filter_name);
|
||||||
|
// check filter name
|
||||||
|
if (name.empty()) {
|
||||||
|
throw std::invalid_argument("filter name is empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
// assign filter patterns
|
||||||
|
FilterModes modes;
|
||||||
|
for (const auto& item : il) {
|
||||||
|
modes.emplace_back(item);
|
||||||
|
}
|
||||||
|
// check filter patterns
|
||||||
|
if (modes.empty()) {
|
||||||
|
throw std::invalid_argument("filter pattern list is empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
// add into pairs and return
|
||||||
|
m_Filters.emplace_back(std::make_pair(std::move(name), std::move(modes)));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t FileFilters::get_count() const {
|
||||||
|
return m_Filters.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileFilters::clear() {
|
||||||
|
m_Filters.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogResult<WinFileFilters> FileFilters::to_windows() const {
|
||||||
|
// prepare return value
|
||||||
|
WinFileFilters rv;
|
||||||
|
|
||||||
|
// check filter size
|
||||||
|
// if it overflow the maximum value, return.
|
||||||
|
size_t count = m_Filters.size();
|
||||||
|
if (!SAFECAST::try_to<UINT>(count).has_value()) {
|
||||||
|
return std::unexpected(DialogError::TooManyFilters);
|
||||||
|
}
|
||||||
|
|
||||||
|
// build new Windows oriented string vector
|
||||||
|
for (const auto& item : m_Filters) {
|
||||||
|
// convert name to wchar
|
||||||
|
auto win_name = ENC::to_wchar(item.first);
|
||||||
|
if (!win_name.has_value()) {
|
||||||
|
return std::unexpected(DialogError::BadEncoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
// join pattern string and convert to wchar
|
||||||
|
const auto& modes = item.second;
|
||||||
|
auto joined_modes = OP::join(modes.begin(), modes.end(), u8";");
|
||||||
|
auto win_modes = ENC::to_wchar(joined_modes);
|
||||||
|
if (!win_modes.has_value()) {
|
||||||
|
return std::unexpected(DialogError::BadEncoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
// append this pair
|
||||||
|
rv.m_WinFilters.emplace_back(std::make_pair(win_name.value(), win_modes.value()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// update data struct
|
||||||
|
rv.update();
|
||||||
|
|
||||||
|
// okey, return value
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#pragma region WinFileDialog
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Assistant function for making copy of IShellItem*.
|
||||||
|
* @param[in] obj The object for making copy.
|
||||||
|
* @return Copied COM object.
|
||||||
|
*/
|
||||||
|
static WINCOM::SmartIShellItem duplicate_shell_item(const WINCOM::SmartIShellItem& obj) {
|
||||||
|
// COM object is actually like a std::shared_ptr.
|
||||||
|
// So we simply copy its raw pointer and increase it reference counter if it is not nullptr.
|
||||||
|
WINCOM::SmartIShellItem rv(obj.get());
|
||||||
|
if (rv) rv->AddRef();
|
||||||
|
// Okey, return copied object.
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
WinFileDialog::WinFileDialog() :
|
||||||
|
m_WinOwner(NULL), m_WinFileTypes(), m_WinDefaultFileTypeIndex(0u), m_WinTitle(std::nullopt), m_WinInitFileName(std::nullopt),
|
||||||
|
m_WinInitDirectory(nullptr) {}
|
||||||
|
|
||||||
|
WinFileDialog::~WinFileDialog() {}
|
||||||
|
|
||||||
|
YYCC_IMPL_COPY_CTOR(WinFileDialog, rhs) :
|
||||||
|
m_WinOwner(rhs.m_WinOwner), m_WinFileTypes(rhs.m_WinFileTypes), m_WinDefaultFileTypeIndex(rhs.m_WinDefaultFileTypeIndex),
|
||||||
|
m_WinTitle(rhs.m_WinTitle), m_WinInitFileName(rhs.m_WinInitFileName),
|
||||||
|
m_WinInitDirectory(duplicate_shell_item(rhs.m_WinInitDirectory)) {}
|
||||||
|
|
||||||
|
YYCC_IMPL_COPY_OPER(WinFileDialog, rhs) {
|
||||||
|
this->m_WinOwner = rhs.m_WinOwner;
|
||||||
|
this->m_WinFileTypes = rhs.m_WinFileTypes;
|
||||||
|
this->m_WinDefaultFileTypeIndex = rhs.m_WinDefaultFileTypeIndex;
|
||||||
|
this->m_WinTitle = rhs.m_WinTitle;
|
||||||
|
this->m_WinInitFileName = rhs.m_WinInitFileName;
|
||||||
|
this->m_WinInitDirectory = duplicate_shell_item(rhs.m_WinInitDirectory);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WinFileDialog::has_owner() const {
|
||||||
|
return m_WinOwner != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
HWND WinFileDialog::get_owner() const {
|
||||||
|
if (!has_owner()) throw std::logic_error("fetch not set owner");
|
||||||
|
return m_WinOwner;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WinFileDialog::has_title() const {
|
||||||
|
return m_WinTitle.has_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t* WinFileDialog::get_title() const {
|
||||||
|
if (!has_title()) throw std::logic_error("fetch not set title");
|
||||||
|
return m_WinTitle.value().c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WinFileDialog::has_init_file_name() const {
|
||||||
|
return m_WinInitFileName.has_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t* WinFileDialog::get_init_file_name() const {
|
||||||
|
if (!has_init_file_name()) throw std::logic_error("fetch not set init file name");
|
||||||
|
return m_WinInitFileName.value().c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WinFileDialog::has_init_directory() const {
|
||||||
|
return m_WinInitDirectory.get() != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
IShellItem* WinFileDialog::get_init_directory() const {
|
||||||
|
if (!has_init_file_name()) throw std::logic_error("fetch not set init directory");
|
||||||
|
return m_WinInitDirectory.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
const WinFileFilters& WinFileDialog::get_file_types() const {
|
||||||
|
return m_WinFileTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT WinFileDialog::get_default_file_type_index() const {
|
||||||
|
// This index has been checked so we return it directly.
|
||||||
|
return m_WinDefaultFileTypeIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#pragma region FileDialog
|
||||||
|
|
||||||
|
FileDialog::FileDialog() :
|
||||||
|
m_Owner(NULL), m_FileTypes(), m_DefaultFileTypeIndex(0u), m_Title(std::nullopt), m_InitFileName(std::nullopt),
|
||||||
|
m_InitDirectory(std::nullopt) {}
|
||||||
|
|
||||||
|
FileDialog::~FileDialog() {}
|
||||||
|
|
||||||
|
void FileDialog::set_owner(HWND owner) {
|
||||||
|
m_Owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDialog::set_title(const std::u8string_view& title) {
|
||||||
|
m_Title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDialog::unset_title() {
|
||||||
|
m_Title = std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDialog::set_init_file_name(const std::u8string_view& init_filename) {
|
||||||
|
m_InitFileName = init_filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDialog::unset_init_file_name() {
|
||||||
|
m_InitFileName = std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDialog::set_init_directory(const std::u8string_view& init_dir) {
|
||||||
|
m_InitDirectory = init_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDialog::unset_init_directory() {
|
||||||
|
m_InitDirectory = std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileFilters& FileDialog::configre_file_types() {
|
||||||
|
return m_FileTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDialog::set_default_file_type_index(size_t idx) {
|
||||||
|
m_DefaultFileTypeIndex = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDialog::clear() {
|
||||||
|
m_Owner = NULL;
|
||||||
|
m_Title = std::nullopt;
|
||||||
|
m_InitFileName = std::nullopt;
|
||||||
|
m_InitDirectory = std::nullopt;
|
||||||
|
m_FileTypes.clear();
|
||||||
|
m_DefaultFileTypeIndex = 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogResult<WinFileDialog> FileDialog::to_windows() const {
|
||||||
|
// prepare return value
|
||||||
|
WinFileDialog rv;
|
||||||
|
|
||||||
// set owner
|
// set owner
|
||||||
win_result.m_WinOwner = m_Owner;
|
rv.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
|
// build title and init file name
|
||||||
if (m_HasTitle) {
|
if (m_Title.has_value()) {
|
||||||
if (!YYCC::EncodingHelper::UTF8ToWchar(m_Title, win_result.m_WinTitle)) return false;
|
auto win_title = ENC::to_wchar(m_Title.value());
|
||||||
win_result.m_HasTitle = true;
|
if (!win_title.has_value()) return std::unexpected(DialogError::BadEncoding);
|
||||||
}
|
else rv.m_WinTitle = win_title.value();
|
||||||
if (m_HasInitFileName) {
|
} else rv.m_WinTitle = std::nullopt;
|
||||||
if (!YYCC::EncodingHelper::UTF8ToWchar(m_InitFileName, win_result.m_WinInitFileName)) return false;
|
if (m_InitFileName.has_value()) {
|
||||||
win_result.m_HasInitFileName = true;
|
auto win_init_file_name = ENC::to_wchar(m_InitFileName.value());
|
||||||
}
|
if (!win_init_file_name.has_value()) return std::unexpected(DialogError::BadEncoding);
|
||||||
|
else rv.m_WinInitFileName = win_init_file_name.value();
|
||||||
|
} else rv.m_WinInitFileName = std::nullopt;
|
||||||
|
|
||||||
// fetch init directory
|
// fetch init directory
|
||||||
if (m_HasInitDirectory) {
|
if (m_InitDirectory.has_value()) {
|
||||||
// convert to wpath
|
// convert to wchar path
|
||||||
std::wstring w_init_directory;
|
auto w_init_dir = ENC::to_wchar(m_InitDirectory.value());
|
||||||
if (!YYCC::EncodingHelper::UTF8ToWchar(m_InitDirectory, w_init_directory)) return false;
|
if (!w_init_dir.has_value()) {
|
||||||
|
return std::unexpected(DialogError::BadEncoding);
|
||||||
|
}
|
||||||
|
|
||||||
// fetch IShellItem*
|
// fetch IShellItem*
|
||||||
// Ref: https://stackoverflow.com/questions/76306324/how-to-set-default-folder-for-ifileopendialog-interface
|
// Ref: https://stackoverflow.com/questions/76306324/how-to-set-default-folder-for-ifileopendialog-interface
|
||||||
IShellItem* init_directory = NULL;
|
IShellItem* init_directory = NULL;
|
||||||
HRESULT hr = SHCreateItemFromParsingName(w_init_directory.c_str(), NULL, IID_PPV_ARGS(&init_directory));
|
HRESULT hr = SHCreateItemFromParsingName(w_init_dir.value().c_str(), NULL, IID_PPV_ARGS(&init_directory));
|
||||||
if (FAILED(hr)) return false;
|
if (FAILED(hr)) return std::unexpected(DialogError::NoSuchDir);
|
||||||
|
|
||||||
// assign IShellItem*
|
// assign IShellItem*
|
||||||
win_result.m_WinInitDirectory.reset(init_directory);
|
rv.m_WinInitDirectory.reset(init_directory);
|
||||||
|
} else rv.m_WinInitDirectory.reset();
|
||||||
|
|
||||||
|
// build file filters
|
||||||
|
auto win_file_types = m_FileTypes.to_windows();
|
||||||
|
if (!win_file_types.has_value()) return std::unexpected(win_file_types.error());
|
||||||
|
else rv.m_WinFileTypes = std::move(win_file_types.value());
|
||||||
|
|
||||||
|
// check and assign default file type index
|
||||||
|
// check whether it is out of filters range
|
||||||
|
if (m_DefaultFileTypeIndex >= m_FileTypes.get_count()) {
|
||||||
|
return std::unexpected(DialogError::IndexOutOfRange);
|
||||||
}
|
}
|
||||||
|
// check whether it can be safely casted into UINT.
|
||||||
|
auto win_def_index_base0 = SAFECAST::try_to<UINT>(m_DefaultFileTypeIndex);
|
||||||
|
if (!win_def_index_base0.has_value()) {
|
||||||
|
return std::unexpected(DialogError::IndexOverflow);
|
||||||
|
}
|
||||||
|
// check whether it can safely make addition with 1.
|
||||||
|
auto win_def_index = SAFEOP::checked_add<UINT>(win_def_index_base0.value(), 1);
|
||||||
|
if (!win_def_index.has_value()) {
|
||||||
|
return std::unexpected(DialogError::IndexOverflow);
|
||||||
|
}
|
||||||
|
// okey, assign it
|
||||||
|
rv.m_WinDefaultFileTypeIndex = win_def_index.value();
|
||||||
|
|
||||||
// everything is okey
|
// everything is okey
|
||||||
return true;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region Windows Dialog Code
|
#pragma region Exposed Functions
|
||||||
|
|
||||||
enum class CommonFileDialogType { OpenFile, OpenMultipleFiles, SaveFile, OpenFolder };
|
enum class GenericFileDialogType { OpenFile, OpenFiles, SaveFile, OpenFolder };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Extract display name from given IShellItem*.
|
* @brief Extract display name from given IShellItem*.
|
||||||
* @param item[in] The pointer to IShellItem for extracting.
|
* @param[in] item The pointer to IShellItem for extracting.
|
||||||
* @param ret[out] Extracted display name container.
|
* @return Extract display name, or error occurs.
|
||||||
* @return True if success, otherwise false.
|
* @remarks This is an assist function of generic_file_dialog().
|
||||||
* @remarks This is an assist function of CommonFileDialog.
|
|
||||||
*/
|
*/
|
||||||
static bool ExtractDisplayName(IShellItem* item, yycc_u8string& ret) {
|
static DialogResult<std::u8string> extract_display_name(IShellItem* item) {
|
||||||
// fetch display name from IShellItem*
|
// fetch display name from IShellItem*
|
||||||
LPWSTR _name;
|
LPWSTR display_name_ptr;
|
||||||
HRESULT hr = item->GetDisplayName(SIGDN_FILESYSPATH, &_name);
|
HRESULT hr = item->GetDisplayName(SIGDN_FILESYSPATH, &display_name_ptr);
|
||||||
if (FAILED(hr)) return false;
|
if (FAILED(hr)) return std::unexpected(DialogError::BadCOMCall);
|
||||||
COMHelper::SmartLPWSTR display_name(_name);
|
WINCOM::SmartLPWSTR display_name(display_name_ptr);
|
||||||
|
|
||||||
// convert result
|
// convert result and return
|
||||||
if (!YYCC::EncodingHelper::WcharToUTF8(display_name.get(), ret)) return false;
|
return ENC::to_utf8(display_name.get()).transform_error([](auto err) { return DialogError::BadEncoding; });
|
||||||
|
|
||||||
// finished
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief General file dialog.
|
* @brief Generic file dialog.
|
||||||
* @param params[in] User specified parameter controlling the behavior of this file dialog,
|
* @details This function is the real underlying function of all dialog functions.
|
||||||
* including title, file types and etc.
|
* @param[in] params User specified parameter controlling the behavior of this file dialog, including title, file types and etc.
|
||||||
* @param ret[out] The path to user selected files or folders.
|
* @return
|
||||||
* For multiple selection, the count of items >= 1. For other scenario, the count of item is 1.
|
* The full path to user selected files or folders.
|
||||||
* @return True if success, otherwise false (input parameters is wrong or user click "Cancel" in popup window).
|
* For multiple selection, the count of items >= 1. For others, the count of item must be 1.
|
||||||
* @remarks This function is the real underlying function of all dialog functions.
|
* Or nothing (click "Cancel"), or error occurs.
|
||||||
*/
|
*/
|
||||||
template<CommonFileDialogType EDialogType>
|
template<GenericFileDialogType EDialogType>
|
||||||
static bool CommonFileDialog(const FileDialog& params, std::vector<yycc_u8string>& ret) {
|
static DialogResult<DialogOutcome<std::vector<std::u8string>>> generic_file_dialog(const FileDialog& params) {
|
||||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/shell/common-file-dialog
|
// Reference: https://learn.microsoft.com/en-us/windows/win32/shell/common-file-dialog
|
||||||
// prepare result variable
|
// prepare result variable
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
|
// create a const bad com call unexpected result
|
||||||
|
// because it is widely used in this function.
|
||||||
|
constexpr auto BAD_COM_CALL = std::unexpected(DialogError::BadCOMCall);
|
||||||
|
|
||||||
// check whether COM environment has been initialized
|
// check whether COM environment has been initialized
|
||||||
if (!COMHelper::IsInitialized()) return false;
|
if (!WINCOM::is_initialized()) return BAD_COM_CALL;
|
||||||
|
|
||||||
// create file dialog instance
|
// create file dialog instance
|
||||||
// fetch dialog CLSID first
|
// fetch dialog CLSID first
|
||||||
CLSID dialog_clsid;
|
CLSID dialog_clsid;
|
||||||
switch (EDialogType) {
|
switch (EDialogType) {
|
||||||
case CommonFileDialogType::OpenFile:
|
case GenericFileDialogType::OpenFile:
|
||||||
case CommonFileDialogType::OpenMultipleFiles:
|
case GenericFileDialogType::OpenFiles:
|
||||||
case CommonFileDialogType::OpenFolder:
|
case GenericFileDialogType::OpenFolder:
|
||||||
dialog_clsid = CLSID_FileOpenDialog;
|
dialog_clsid = CLSID_FileOpenDialog;
|
||||||
break;
|
break;
|
||||||
case CommonFileDialogType::SaveFile:
|
case GenericFileDialogType::SaveFile:
|
||||||
dialog_clsid = CLSID_FileSaveDialog;
|
dialog_clsid = CLSID_FileSaveDialog;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
throw std::invalid_argument("unknown dialog type");
|
||||||
}
|
}
|
||||||
// create raw dialog pointer
|
// create raw dialog pointer
|
||||||
IFileDialog* _pfd = nullptr;
|
IFileDialog* raw_pfd = nullptr;
|
||||||
hr = CoCreateInstance(dialog_clsid, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&_pfd));
|
hr = CoCreateInstance(dialog_clsid, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&raw_pfd));
|
||||||
if (FAILED(hr)) return false;
|
if (FAILED(hr)) return BAD_COM_CALL;
|
||||||
// create memory-safe dialog pointer
|
// create memory-safe dialog pointer
|
||||||
COMHelper::SmartIFileDialog pfd(_pfd);
|
WINCOM::SmartIFileDialog pfd(raw_pfd);
|
||||||
|
|
||||||
// set options for dialog
|
// set options for dialog
|
||||||
// before setting, always get the options first in order.
|
// before setting, always get the options first in order.
|
||||||
// not to override existing options.
|
// not to override existing options.
|
||||||
DWORD dwFlags;
|
DWORD dwFlags;
|
||||||
hr = pfd->GetOptions(&dwFlags);
|
hr = pfd->GetOptions(&dwFlags);
|
||||||
if (FAILED(hr)) return false;
|
if (FAILED(hr)) return BAD_COM_CALL;
|
||||||
// modify options
|
// modify options
|
||||||
switch (EDialogType) {
|
switch (EDialogType) {
|
||||||
// We want user only can pick file system files: FOS_FORCEFILESYSTEM.
|
// We want user only can pick file system files: FOS_FORCEFILESYSTEM.
|
||||||
// Open dialog default: FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR
|
// Open dialog default: FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR
|
||||||
// Save dialog default: FOS_OVERWRITEPROMPT | FOS_NOREADONLYRETURN | FOS_PATHMUSTEXIST | FOS_NOCHANGEDIR
|
// Save dialog default: FOS_OVERWRITEPROMPT | FOS_NOREADONLYRETURN | FOS_PATHMUSTEXIST | FOS_NOCHANGEDIR
|
||||||
// Pick folder: FOS_PICKFOLDERS
|
// Pick folder: FOS_PICKFOLDERS
|
||||||
case CommonFileDialogType::OpenFile:
|
case GenericFileDialogType::OpenFile:
|
||||||
dwFlags |= FOS_FORCEFILESYSTEM;
|
dwFlags |= FOS_FORCEFILESYSTEM;
|
||||||
dwFlags |= FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR;
|
dwFlags |= FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR;
|
||||||
break;
|
break;
|
||||||
case CommonFileDialogType::OpenMultipleFiles:
|
case GenericFileDialogType::OpenFiles:
|
||||||
dwFlags |= FOS_FORCEFILESYSTEM;
|
dwFlags |= FOS_FORCEFILESYSTEM;
|
||||||
dwFlags |= FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR;
|
dwFlags |= FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR;
|
||||||
dwFlags |= FOS_ALLOWMULTISELECT;
|
dwFlags |= FOS_ALLOWMULTISELECT;
|
||||||
break;
|
break;
|
||||||
case CommonFileDialogType::SaveFile:
|
case GenericFileDialogType::SaveFile:
|
||||||
dwFlags |= FOS_FORCEFILESYSTEM;
|
dwFlags |= FOS_FORCEFILESYSTEM;
|
||||||
dwFlags |= FOS_OVERWRITEPROMPT | FOS_NOREADONLYRETURN | FOS_PATHMUSTEXIST | FOS_NOCHANGEDIR;
|
dwFlags |= FOS_OVERWRITEPROMPT | FOS_NOREADONLYRETURN | FOS_PATHMUSTEXIST | FOS_NOCHANGEDIR;
|
||||||
break;
|
break;
|
||||||
case CommonFileDialogType::OpenFolder:
|
case GenericFileDialogType::OpenFolder:
|
||||||
dwFlags |= FOS_FORCEFILESYSTEM;
|
dwFlags |= FOS_FORCEFILESYSTEM;
|
||||||
dwFlags |= FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR;
|
dwFlags |= FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR;
|
||||||
dwFlags |= FOS_PICKFOLDERS;
|
dwFlags |= FOS_PICKFOLDERS;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
throw std::invalid_argument("unknown dialog type");
|
||||||
}
|
}
|
||||||
// set folder dialog options
|
// set folder dialog options
|
||||||
hr = pfd->SetOptions(dwFlags);
|
hr = pfd->SetOptions(dwFlags);
|
||||||
if (FAILED(hr)) return false;
|
if (FAILED(hr)) return BAD_COM_CALL;
|
||||||
|
|
||||||
// build Windows used file dialog parameters
|
// build Windows used file dialog parameters
|
||||||
WinFileDialog win_params;
|
auto pending_win_params = params.to_windows();
|
||||||
if (!params.Generate(win_params)) return false;
|
if (!pending_win_params.has_value()) return std::unexpected(pending_win_params.error());
|
||||||
|
WinFileDialog win_params(std::move(pending_win_params.value()));
|
||||||
|
|
||||||
// setup title and init file name
|
// setup title and init file name
|
||||||
if (win_params.HasTitle()) {
|
if (win_params.has_title()) {
|
||||||
hr = pfd->SetTitle(win_params.GetTitle());
|
hr = pfd->SetTitle(win_params.get_title());
|
||||||
if (FAILED(hr)) return false;
|
if (FAILED(hr)) return BAD_COM_CALL;
|
||||||
}
|
}
|
||||||
if (win_params.HasInitFileName()) {
|
if (win_params.has_init_file_name()) {
|
||||||
hr = pfd->SetFileName(win_params.GetInitFileName());
|
hr = pfd->SetFileName(win_params.get_init_file_name());
|
||||||
if (FAILED(hr)) return false;
|
if (FAILED(hr)) return BAD_COM_CALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup init directory
|
// setup init directory
|
||||||
if (win_params.HasInitDirectory()) {
|
if (win_params.has_init_directory()) {
|
||||||
hr = pfd->SetFolder(win_params.GetInitDirectory());
|
hr = pfd->SetFolder(win_params.get_init_directory());
|
||||||
|
if (FAILED(hr)) return BAD_COM_CALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set file types and default file index when we picking file
|
// set file types and default file index when we picking file
|
||||||
if constexpr (EDialogType != CommonFileDialogType::OpenFolder) {
|
if constexpr (EDialogType != GenericFileDialogType::OpenFolder) {
|
||||||
// set file types list
|
// set file types list
|
||||||
const auto& file_filters = win_params.GetFileTypes();
|
const auto& file_filters = win_params.get_file_types();
|
||||||
hr = pfd->SetFileTypes(file_filters.GetFilterCount(), file_filters.GetFilterSpecs());
|
hr = pfd->SetFileTypes(file_filters.get_filter_count(), file_filters.get_filter_specs());
|
||||||
if (FAILED(hr)) return false;
|
if (FAILED(hr)) return BAD_COM_CALL;
|
||||||
|
|
||||||
// set default file type index
|
// set default file type index
|
||||||
hr = pfd->SetFileTypeIndex(win_params.GetDefaultFileTypeIndex());
|
hr = pfd->SetFileTypeIndex(win_params.get_default_file_type_index());
|
||||||
if (FAILED(hr)) return false;
|
if (FAILED(hr)) return BAD_COM_CALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// show the dialog
|
// show the dialog and return if user click "Cancel"
|
||||||
hr = pfd->Show(win_params.HasOwner() ? win_params.GetOwner() : nullptr);
|
hr = pfd->Show(win_params.has_owner() ? win_params.get_owner() : NULL);
|
||||||
if (FAILED(hr)) return false;
|
if (FAILED(hr)) return std::nullopt;
|
||||||
|
|
||||||
|
// prepare return value
|
||||||
|
std::vector<std::u8string> rv;
|
||||||
// obtain result when user click "OK" button.
|
// obtain result when user click "OK" button.
|
||||||
switch (EDialogType) {
|
switch (EDialogType) {
|
||||||
case CommonFileDialogType::OpenFile:
|
case GenericFileDialogType::OpenFile:
|
||||||
case CommonFileDialogType::OpenFolder:
|
case GenericFileDialogType::OpenFolder:
|
||||||
case CommonFileDialogType::SaveFile: {
|
case GenericFileDialogType::SaveFile: {
|
||||||
// obtain one file entry
|
// obtain one file entry
|
||||||
IShellItem* _item;
|
IShellItem* raw_item;
|
||||||
hr = pfd->GetResult(&_item);
|
hr = pfd->GetResult(&raw_item);
|
||||||
if (FAILED(hr)) return false;
|
if (FAILED(hr)) return BAD_COM_CALL;
|
||||||
COMHelper::SmartIShellItem result_item(_item);
|
WINCOM::SmartIShellItem item(raw_item);
|
||||||
|
|
||||||
// extract display name
|
// extract display name
|
||||||
yycc_u8string result_name;
|
auto display_name = extract_display_name(item.get());
|
||||||
if (!ExtractDisplayName(result_item.get(), result_name)) return false;
|
if (!display_name.has_value()) return std::unexpected(display_name.error());
|
||||||
|
|
||||||
// append result
|
// append result
|
||||||
ret.emplace_back(std::move(result_name));
|
rv.emplace_back(std::move(display_name.value()));
|
||||||
} break;
|
} break;
|
||||||
case CommonFileDialogType::OpenMultipleFiles: {
|
case GenericFileDialogType::OpenFiles: {
|
||||||
// try casting file dialog to file open dialog
|
// try casting file dialog to file open dialog
|
||||||
// Ref: https://learn.microsoft.com/en-us/windows/win32/learnwin32/asking-an-object-for-an-interface
|
// Ref: https://learn.microsoft.com/en-us/windows/win32/learnwin32/asking-an-object-for-an-interface
|
||||||
IFileOpenDialog* _pfod = nullptr;
|
IFileOpenDialog* raw_pfod = nullptr;
|
||||||
hr = pfd->QueryInterface(IID_PPV_ARGS(&_pfod));
|
hr = pfd->QueryInterface(IID_PPV_ARGS(&raw_pfod));
|
||||||
if (FAILED(hr)) return false;
|
if (FAILED(hr)) return BAD_COM_CALL;
|
||||||
COMHelper::SmartIFileOpenDialog pfod(_pfod);
|
WINCOM::SmartIFileOpenDialog pfod(raw_pfod);
|
||||||
|
|
||||||
// obtain multiple file entires
|
// obtain multiple file entires
|
||||||
IShellItemArray* _items;
|
IShellItemArray* raw_items;
|
||||||
hr = pfod->GetResults(&_items);
|
hr = pfod->GetResults(&raw_items);
|
||||||
if (FAILED(hr)) return false;
|
if (FAILED(hr)) return BAD_COM_CALL;
|
||||||
COMHelper::SmartIShellItemArray result_items(_items);
|
WINCOM::SmartIShellItemArray items(raw_items);
|
||||||
|
|
||||||
// analyze file entries
|
// analyze file entries
|
||||||
// get array count first
|
// get array count first
|
||||||
DWORD result_items_count = 0u;
|
DWORD items_count = 0u;
|
||||||
hr = result_items->GetCount(&result_items_count);
|
hr = items->GetCount(&items_count);
|
||||||
if (FAILED(hr)) return false;
|
if (FAILED(hr)) return BAD_COM_CALL;
|
||||||
// iterate array
|
// iterate array
|
||||||
for (DWORD i = 0u; i < result_items_count; ++i) {
|
for (DWORD i = 0u; i < items_count; ++i) {
|
||||||
// fetch item by index
|
// fetch item by index
|
||||||
IShellItem* _item;
|
IShellItem* raw_item;
|
||||||
;
|
hr = items->GetItemAt(i, &raw_item);
|
||||||
hr = result_items->GetItemAt(i, &_item);
|
if (FAILED(hr)) return BAD_COM_CALL;
|
||||||
if (FAILED(hr)) return false;
|
WINCOM::SmartIShellItem item(raw_item);
|
||||||
COMHelper::SmartIShellItem result_item(_item);
|
|
||||||
|
|
||||||
// extract display name
|
// extract display name
|
||||||
yycc_u8string result_name;
|
auto display_name = extract_display_name(item.get());
|
||||||
if (!ExtractDisplayName(result_item.get(), result_name)) return false;
|
if (!display_name.has_value()) return std::unexpected(display_name.error());
|
||||||
|
|
||||||
// append result
|
// append result
|
||||||
ret.emplace_back(std::move(result_name));
|
rv.emplace_back(std::move(display_name.value()));
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
default:
|
default:
|
||||||
return false;
|
throw std::invalid_argument("unknown dialog type");
|
||||||
}
|
}
|
||||||
|
|
||||||
// everything is okey
|
// everything is okey
|
||||||
return true;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region Wrapper Functions
|
#pragma region Wrapper Functions
|
||||||
|
|
||||||
bool OpenFileDialog(const FileDialog& params, yycc_u8string& ret) {
|
/**
|
||||||
std::vector<yycc_u8string> cache;
|
* @brief Assistant function for extracting item from given value returned by generic file dialog.
|
||||||
bool isok = CommonFileDialog<CommonFileDialogType::OpenFile>(params, cache);
|
* @details This function will check whether inner vector only contain one item.
|
||||||
if (isok) ret = cache.front();
|
* @param[in] rhs The return value to be extracted.
|
||||||
return isok;
|
* @return Extracted return value.
|
||||||
|
*/
|
||||||
|
static DialogResult<DialogOutcome<std::u8string>> transform_generic_rv(DialogResult<DialogOutcome<std::vector<std::u8string>>>&& rhs) {
|
||||||
|
if (rhs.has_value()) {
|
||||||
|
const auto& inner = rhs.value();
|
||||||
|
if (inner.has_value()) {
|
||||||
|
const auto& vec = inner.value();
|
||||||
|
if (vec.size() != 1u) throw std::logic_error("return value doesn't contain exactly one item");
|
||||||
|
else return std::move(vec.front());
|
||||||
|
} else {
|
||||||
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
bool OpenMultipleFileDialog(const FileDialog& params, std::vector<yycc_u8string>& ret) {
|
} else {
|
||||||
return CommonFileDialog<CommonFileDialogType::OpenMultipleFiles>(params, ret);
|
return std::unexpected(rhs.error());
|
||||||
}
|
}
|
||||||
bool SaveFileDialog(const FileDialog& params, yycc_u8string& ret) {
|
|
||||||
std::vector<yycc_u8string> cache;
|
|
||||||
bool isok = CommonFileDialog<CommonFileDialogType::SaveFile>(params, cache);
|
|
||||||
if (isok) ret = cache.front();
|
|
||||||
return isok;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenFolderDialog(const FileDialog& params, yycc_u8string& ret) {
|
DialogResult<DialogOutcome<std::u8string>> open_file(const FileDialog& params){
|
||||||
std::vector<yycc_u8string> cache;
|
return transform_generic_rv(generic_file_dialog<GenericFileDialogType::OpenFile>(params));
|
||||||
bool isok = CommonFileDialog<CommonFileDialogType::OpenFolder>(params, cache);
|
}
|
||||||
if (isok) ret = cache.front();
|
|
||||||
return isok;
|
DialogResult<DialogOutcome<std::vector<std::u8string>>> open_files(const FileDialog& params) {
|
||||||
|
return generic_file_dialog<GenericFileDialogType::OpenFiles>(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogResult<DialogOutcome<std::u8string>> save_file(const FileDialog& params) {
|
||||||
|
return transform_generic_rv(generic_file_dialog<GenericFileDialogType::SaveFile>(params));
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogResult<DialogOutcome<std::u8string>> open_folder(const FileDialog& params) {
|
||||||
|
return transform_generic_rv(generic_file_dialog<GenericFileDialogType::OpenFolder>(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
@ -6,14 +6,19 @@
|
|||||||
#include "../macro/class_copy_move.hpp"
|
#include "../macro/class_copy_move.hpp"
|
||||||
#include "com.hpp"
|
#include "com.hpp"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
|
#include <expected>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include "import_guard_head.hpp"
|
#include "import_guard_head.hpp"
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <shlobj_core.h>
|
#include <shlobj_core.h>
|
||||||
#include "import_guard_tail.hpp"
|
#include "import_guard_tail.hpp"
|
||||||
|
|
||||||
|
#define NS_YYCC_WINDOWS_COM ::yycc::windows::com
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The namespace providing Windows universal dialog features.
|
* @brief The namespace providing Windows universal dialog features.
|
||||||
* @details
|
* @details
|
||||||
@ -22,39 +27,62 @@
|
|||||||
*/
|
*/
|
||||||
namespace yycc::windows::dialog {
|
namespace yycc::windows::dialog {
|
||||||
|
|
||||||
|
/// @brief The error occurs in this module.
|
||||||
|
enum class DialogError {
|
||||||
|
BadEncoding, ///< Error occurs when perform encoding convertion.
|
||||||
|
TooManyFilters, ///< The size of file filters list is too large for Windows.
|
||||||
|
IndexOverflow, ///< Default filter index is too large for Windows.
|
||||||
|
IndexOutOfRange, ///< Default filter index is out of range of filters list.
|
||||||
|
NoSuchDir, ///< Given initial directory path is invalid.
|
||||||
|
BadCOMCall, ///< Some COM function calls failed.
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief The result type used in this module.
|
||||||
|
template<typename T>
|
||||||
|
using DialogResult = std::expected<T, DialogError>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @brief The class representing the file types region in file dialog.
|
* @brief The class representing the file types region in file dialog.
|
||||||
* @details
|
* @details
|
||||||
* This class is served for Windows used.
|
* This class is private and served for Windows used.
|
||||||
* Programmer should \b not create this class manually.
|
* Programmer should \b not create this class manually.
|
||||||
*/
|
*/
|
||||||
class WinFileFilters {
|
class WinFileFilters {
|
||||||
friend class FileFilters;
|
friend class FileFilters;
|
||||||
friend class WinFileDialog;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WinFileFilters() : m_WinFilters(), m_WinDataStruct(nullptr) {}
|
|
||||||
YYCC_DELETE_COPY_MOVE(WinFileFilters)
|
|
||||||
|
|
||||||
/// @brief Get the count of available file filters
|
|
||||||
UINT GetFilterCount() const { return static_cast<UINT>(m_WinFilters.size()); }
|
|
||||||
/// @brief Get pointer to Windows used file filters declarations
|
|
||||||
const COMDLG_FILTERSPEC* GetFilterSpecs() const { return m_WinDataStruct.get(); }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
using WinFilterModes = std::wstring;
|
using WinFilterModes = std::wstring;
|
||||||
using WinFilterName = std::wstring;
|
using WinFilterName = std::wstring;
|
||||||
using WinFilterPair = std::pair<WinFilterName, WinFilterModes>;
|
using WinFilterPair = std::pair<WinFilterName, WinFilterModes>;
|
||||||
|
|
||||||
std::vector<WinFilterPair> m_WinFilters;
|
public:
|
||||||
std::unique_ptr<COMDLG_FILTERSPEC[]> m_WinDataStruct;
|
WinFileFilters();
|
||||||
|
~WinFileFilters();
|
||||||
|
YYCC_DECL_COPY_MOVE(WinFileFilters)
|
||||||
|
|
||||||
/// @brief Clear all current file filters
|
public:
|
||||||
void Clear() {
|
/**
|
||||||
m_WinDataStruct.reset();
|
* @brief Get the count of available file filters
|
||||||
m_WinFilters.clear();
|
* @return Count of file filters.
|
||||||
}
|
*/
|
||||||
|
UINT get_filter_count() const;
|
||||||
|
/**
|
||||||
|
* @brief Get pointer to Windows used file filters declarations
|
||||||
|
* @return Pointer for Windows use.
|
||||||
|
*/
|
||||||
|
const COMDLG_FILTERSPEC* get_filter_specs() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief Update COMDLG_FILTERSPEC according to file filter list.
|
||||||
|
* @remarks Programmer \b MUST call this function after you modify m_WinFilters.
|
||||||
|
*/
|
||||||
|
void update();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<WinFilterPair> m_WinFilters;
|
||||||
|
std::vector<COMDLG_FILTERSPEC> m_WinDataStruct;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -62,52 +90,51 @@ namespace yycc::windows::dialog {
|
|||||||
* @details
|
* @details
|
||||||
* This class is served for programmer using.
|
* This class is served for programmer using.
|
||||||
* But you don't need create it on your own.
|
* But you don't need create it on your own.
|
||||||
* You can simply fetch it by FileDialog::ConfigreFileTypes ,
|
* You can simply fetch it by FileDialog::ConfigreFileTypes(),
|
||||||
* because this class is a part of FileDialog.
|
* because this class is a part of FileDialog.
|
||||||
*/
|
*/
|
||||||
class FileFilters {
|
class FileFilters {
|
||||||
public:
|
public:
|
||||||
FileFilters() : m_Filters() {}
|
using FilterModes = std::vector<std::u8string>;
|
||||||
YYCC_DELETE_COPY_MOVE(FileFilters)
|
using FilterName = std::u8string;
|
||||||
|
using FilterPair = std::pair<FilterName, FilterModes>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FileFilters();
|
||||||
|
~FileFilters();
|
||||||
|
YYCC_DEFAULT_COPY_MOVE(FileFilters)
|
||||||
|
|
||||||
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Add a filter pair in file types list.
|
* @brief Add a filter pair in file types list.
|
||||||
* @param[in] filter_name The friendly name of the filter.
|
* @param[in] filter_name The friendly name of the filter.
|
||||||
* @param[in] il
|
* @param[in] il
|
||||||
* A C++ initialize list containing acceptable file filter pattern.
|
* A C++ initialize list containing acceptable file filter pattern.
|
||||||
* Every entries must be `const yycc_char8_t*` representing a single filter pattern.
|
* Every entries must be a string representing a single filter pattern.
|
||||||
* The list at least should have one valid pattern.
|
* This list at least should have one pattern.
|
||||||
* This function will not validate these filter patterns, so please write them carefully.
|
|
||||||
* @return True if added success, otherwise false.
|
* @return True if added success, otherwise false.
|
||||||
* @remarks
|
* @warning This function will not validate the content of these filter patterns, so please write them carefully.
|
||||||
* This function allow you register multiple filter patterns for single friendly name.
|
* @exception std::invalid_argument Given filtern name is blank, or filter patterns is empty.
|
||||||
* For example: <TT>Add(u8"Microsoft Word (*.doc; *.docx)", {u8"*.doc", u8"*.docx"})</TT>
|
|
||||||
*/
|
*/
|
||||||
bool Add(const yycc_char8_t* filter_name, std::initializer_list<const yycc_char8_t*> il);
|
void add_filter(const std::u8string_view& filter_name, std::initializer_list<std::u8string_view> il);
|
||||||
/**
|
/**
|
||||||
* @brief Get the count of added filter pairs.
|
* @brief Get the count of added file filters.
|
||||||
* @return The count of already added filter pairs.
|
* @return The count of added file filters.
|
||||||
*/
|
*/
|
||||||
size_t Count() const { return m_Filters.size(); }
|
size_t get_count() const;
|
||||||
|
/**
|
||||||
/// @brief Clear filter pairs for following re-use.
|
* @brief Clear filter pairs for following re-use.
|
||||||
void Clear() { m_Filters.clear(); }
|
*/
|
||||||
|
void clear();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Generate Windows dialog system used data struct.
|
* @private
|
||||||
* @param[out] win_result The class receiving the generated filter data struct.
|
* @brief Build Windows used file filters struct.
|
||||||
* @return True if generation success, otherwise false.
|
* @return Built Windows used struct, or error occurs.
|
||||||
* @remarks
|
|
||||||
* Programmer should not call this function,
|
|
||||||
* this function is used as YYCC internal code.
|
|
||||||
*/
|
*/
|
||||||
bool Generate(WinFileFilters& win_result) const;
|
DialogResult<WinFileFilters> to_windows() const;
|
||||||
|
|
||||||
protected:
|
|
||||||
using FilterModes = std::vector<std::u8string>;
|
|
||||||
using FilterName = std::u8string;
|
|
||||||
using FilterPair = std::pair<FilterName, FilterModes>;
|
|
||||||
|
|
||||||
|
private:
|
||||||
std::vector<FilterPair> m_Filters;
|
std::vector<FilterPair> m_Filters;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -122,36 +149,33 @@ namespace yycc::windows::dialog {
|
|||||||
friend class FileDialog;
|
friend class FileDialog;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WinFileDialog() :
|
WinFileDialog();
|
||||||
m_WinOwner(NULL), m_WinFileTypes(), m_WinDefaultFileTypeIndex(0u), m_HasTitle(false), m_HasInitFileName(false), m_WinTitle(),
|
~WinFileDialog();
|
||||||
m_WinInitFileName(), m_WinInitDirectory(nullptr) {}
|
YYCC_DECL_COPY(WinFileDialog)
|
||||||
YYCC_DELETE_COPY_MOVE(WinFileDialog)
|
YYCC_DEFAULT_MOVE(WinFileDialog)
|
||||||
|
|
||||||
/// @brief Get whether this dialog has owner.
|
/// @brief Get whether this dialog has owner.
|
||||||
bool HasOwner() const { return m_WinOwner != NULL; }
|
bool has_owner() const;
|
||||||
/// @brief Get the \c HWND of dialog owner.
|
/// @brief Get the \c HWND of dialog owner.
|
||||||
HWND GetOwner() const { return m_WinOwner; }
|
HWND get_owner() const;
|
||||||
|
|
||||||
/// @brief Get the struct holding Windows used file filters data.
|
|
||||||
const WinFileFilters& GetFileTypes() const { return m_WinFileTypes; }
|
|
||||||
/// @brief Get the index of default selected file filter.
|
|
||||||
UINT GetDefaultFileTypeIndex() const { return m_WinDefaultFileTypeIndex; }
|
|
||||||
|
|
||||||
/// @brief Get whether dialog has custom title.
|
/// @brief Get whether dialog has custom title.
|
||||||
bool HasTitle() const { return m_HasTitle; }
|
bool has_title() const;
|
||||||
/// @brief Get custom title of dialog.
|
/// @brief Get custom title of dialog.
|
||||||
const wchar_t* GetTitle() const { return m_WinTitle.c_str(); }
|
const wchar_t* get_title() const;
|
||||||
/// @brief Get whether dialog has custom initial file name.
|
/// @brief Get whether dialog has custom initial file name.
|
||||||
bool HasInitFileName() const { return m_HasInitFileName; }
|
bool has_init_file_name() const;
|
||||||
/// @brief Get custom initial file name of dialog
|
/// @brief Get custom initial file name of dialog
|
||||||
const wchar_t* GetInitFileName() const { return m_WinInitFileName.c_str(); }
|
const wchar_t* get_init_file_name() const;
|
||||||
|
|
||||||
/// @brief Get whether dialog has custom initial directory.
|
/// @brief Get whether dialog has custom initial directory.
|
||||||
bool HasInitDirectory() const { return m_WinInitDirectory.get() != nullptr; }
|
bool has_init_directory() const;
|
||||||
/// @brief Get custom initial directory of dialog.
|
/// @brief Get custom initial directory of dialog.
|
||||||
IShellItem* GetInitDirectory() const { return m_WinInitDirectory.get(); }
|
IShellItem* get_init_directory() const;
|
||||||
|
/// @brief Get the struct holding Windows used file filters data.
|
||||||
|
const WinFileFilters& get_file_types() const;
|
||||||
|
/// @brief Get the index of default selected file filter.
|
||||||
|
UINT get_default_file_type_index() const;
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
HWND m_WinOwner;
|
HWND m_WinOwner;
|
||||||
WinFileFilters m_WinFileTypes;
|
WinFileFilters m_WinFileTypes;
|
||||||
/**
|
/**
|
||||||
@ -162,20 +186,8 @@ namespace yycc::windows::dialog {
|
|||||||
* Because the same field located in FileDialog is 0-based index.
|
* Because the same field located in FileDialog is 0-based index.
|
||||||
*/
|
*/
|
||||||
UINT m_WinDefaultFileTypeIndex;
|
UINT m_WinDefaultFileTypeIndex;
|
||||||
bool m_HasTitle, m_HasInitFileName;
|
std::optional<std::wstring> m_WinTitle, m_WinInitFileName;
|
||||||
std::wstring m_WinTitle, m_WinInitFileName;
|
NS_YYCC_WINDOWS_COM::SmartIShellItem m_WinInitDirectory;
|
||||||
com::SmartIShellItem m_WinInitDirectory;
|
|
||||||
|
|
||||||
/// @brief Clear all data and reset them to default value.
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -186,79 +198,71 @@ namespace yycc::windows::dialog {
|
|||||||
*/
|
*/
|
||||||
class FileDialog {
|
class FileDialog {
|
||||||
public:
|
public:
|
||||||
FileDialog() :
|
FileDialog();
|
||||||
m_Owner(NULL), m_FileTypes(), m_DefaultFileTypeIndex(0u), m_Title(), m_InitFileName(), m_InitDirectory(), m_HasTitle(false),
|
~FileDialog();
|
||||||
m_HasInitFileName(false), m_HasInitDirectory(false) {}
|
YYCC_DEFAULT_COPY_MOVE(FileDialog)
|
||||||
YYCC_DELETE_COPY_MOVE(FileDialog)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set the owner of dialog.
|
* @brief Set the owner of dialog.
|
||||||
* @param[in] owner The \c HWND pointing to the owner of dialog, or NULL to remove owner.
|
* @param[in] owner The \c HWND pointing to the owner of dialog, or NULL to remove owner.
|
||||||
*/
|
*/
|
||||||
void SetOwner(HWND owner) { m_Owner = owner; }
|
void set_owner(HWND owner);
|
||||||
/**
|
/**
|
||||||
* @brief Set custom title of dialog
|
* @brief Set custom title of dialog
|
||||||
* @param[in] title The string pointer to custom title, or nullptr to remove it.
|
* @param[in] title The string pointer to custom title.
|
||||||
*/
|
*/
|
||||||
void SetTitle(const yycc_char8_t* title) {
|
void set_title(const std::u8string_view& title);
|
||||||
if (m_HasTitle = title != nullptr) m_Title = title;
|
/**
|
||||||
}
|
* @brief Remove custom title of dialog (keep system default)
|
||||||
|
*/
|
||||||
|
void unset_title();
|
||||||
|
/**
|
||||||
|
* @brief Set the initial file name of dialog
|
||||||
|
* @details If set, the file name will always be same one when opening dialog.
|
||||||
|
* @param[in] init_filename String pointer to initial file name.
|
||||||
|
*/
|
||||||
|
void set_init_file_name(const std::u8string_view& init_filename);
|
||||||
|
/**
|
||||||
|
* @brief Remove custom initial file name of dialog (keep system default)
|
||||||
|
*/
|
||||||
|
void unset_init_file_name();
|
||||||
|
/**
|
||||||
|
* @brief Set the initial directory of dialog
|
||||||
|
* @details If set, the opended directory will always be the same one when opening dialog
|
||||||
|
* @param[in] init_dir String pointer to initial directory.
|
||||||
|
*/
|
||||||
|
void set_init_directory(const std::u8string_view& init_dir);
|
||||||
|
/**
|
||||||
|
* @brief Remove custom initial directory of dialog (keep system default)
|
||||||
|
*/
|
||||||
|
void unset_init_directory();
|
||||||
/**
|
/**
|
||||||
* @brief Fetch the struct describing file filters for future configuration.
|
* @brief Fetch the struct describing file filters for future configuration.
|
||||||
* @return The reference to the struct describing file filters.
|
* @return The reference to the struct describing file filters.
|
||||||
*/
|
*/
|
||||||
FileFilters& ConfigreFileTypes() { return m_FileTypes; }
|
FileFilters& configre_file_types();
|
||||||
/**
|
/**
|
||||||
* @brief Set the index of default selected file filter.
|
* @brief Set the index of default selected file filter.
|
||||||
* @param[in] idx
|
* @param[in] idx
|
||||||
* The index to default one.
|
* The index to default file filter.
|
||||||
* This must be a valid index in file filters.
|
* This must be a valid index in file filters.
|
||||||
*/
|
*/
|
||||||
void SetDefaultFileTypeIndex(size_t idx) { m_DefaultFileTypeIndex = idx; }
|
void set_default_file_type_index(size_t idx);
|
||||||
/**
|
/**
|
||||||
* @brief Set the initial file name of dialog
|
* @brief Clear file dialog parameters for following re-use.
|
||||||
* @details If set, the file name will always be same one when opening dialog.
|
|
||||||
* @param[in] init_filename String pointer to initial file name, or nullptr to remove it.
|
|
||||||
*/
|
*/
|
||||||
void SetInitFileName(const yycc_char8_t* init_filename) {
|
void clear();
|
||||||
if (m_HasInitFileName = init_filename != nullptr) m_InitFileName = init_filename;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief Set the initial directory of dialog
|
|
||||||
* @details If set, the opended directory will always be the same one when opening dialog
|
|
||||||
* @param[in] init_dir
|
|
||||||
* String pointer to initial directory.
|
|
||||||
* Invalid path or nullptr will remove this feature.
|
|
||||||
*/
|
|
||||||
void SetInitDirectory(const yycc_char8_t* init_dir) {
|
|
||||||
if (m_HasInitDirectory = init_dir != nullptr) m_InitDirectory = init_dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Clear file dialog parameters for following re-use.
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Generate Windows dialog system used data struct.
|
* @private
|
||||||
* @param[out] win_result The class receiving the generated filter data struct.
|
* @brief Build Windows used dialog info struct.
|
||||||
* @return True if generation is success, otherwise false.
|
* @return Built Windows used struct, or error occurs.
|
||||||
* @remarks
|
|
||||||
* Programmer should not call this function.
|
|
||||||
* This function is used as YYCC internal code.
|
|
||||||
*/
|
*/
|
||||||
bool Generate(WinFileDialog& win_result) const;
|
DialogResult<WinFileDialog> to_windows() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
HWND m_Owner;
|
HWND m_Owner;
|
||||||
bool m_HasTitle, m_HasInitFileName, m_HasInitDirectory;
|
std::optional<std::u8string> m_Title, m_InitFileName, m_InitDirectory;
|
||||||
std::u8string m_Title, m_InitFileName, m_InitDirectory;
|
|
||||||
FileFilters m_FileTypes;
|
FileFilters m_FileTypes;
|
||||||
/**
|
/**
|
||||||
* @brief The default selected file type in dialog
|
* @brief The default selected file type in dialog
|
||||||
@ -270,35 +274,43 @@ namespace yycc::windows::dialog {
|
|||||||
size_t m_DefaultFileTypeIndex;
|
size_t m_DefaultFileTypeIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The result after user close the dialog.
|
||||||
|
* @details
|
||||||
|
* It is an alias to \c std::optional.
|
||||||
|
* If it do not have value, it means that user click "Cancel" button.
|
||||||
|
* otherwise it contain a value that user selected in dialog.
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
using DialogOutcome = std::optional<T>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Open the dialog which order user select single file to open.
|
* @brief Open the dialog which order user select single file to open.
|
||||||
* @param[in] params The configuration of dialog.
|
* @param[in] params The configuration of dialog.
|
||||||
* @param[out] ret Full path to user selected file.
|
* @return Full path to user selected file, or nothing (click "Cancel"), or error occurs.
|
||||||
* @return False if user calcel the operation or something went wrong, otherwise true.
|
|
||||||
*/
|
*/
|
||||||
bool OpenFileDialog(const FileDialog& params, std::u8string& ret);
|
DialogResult<DialogOutcome<std::u8string>> open_file(const FileDialog& params);
|
||||||
/**
|
/**
|
||||||
* @brief Open the dialog which order user select multiple file to open.
|
* @brief Open the dialog which order user select multiple file to open.
|
||||||
* @param[in] params The configuration of dialog.
|
* @param[in] params The configuration of dialog.
|
||||||
* @param[out] ret The list of full path of user selected files.
|
* @return The list of full path of user selected files, or nothing (click "Cancel"), or error occurs.
|
||||||
* @return False if user calcel the operation or something went wrong, otherwise true.
|
|
||||||
*/
|
*/
|
||||||
bool OpenMultipleFileDialog(const FileDialog& params, std::vector<std::u8string>& ret);
|
DialogResult<DialogOutcome<std::vector<std::u8string>>> open_files(const FileDialog& params);
|
||||||
/**
|
/**
|
||||||
* @brief Open the dialog which order user select single file to save.
|
* @brief Open the dialog which order user select single file to save.
|
||||||
* @param[in] params The configuration of dialog.
|
* @param[in] params The configuration of dialog.
|
||||||
* @param[out] ret Full path to user selected file.
|
* @return Full path to user selected file, or nothing (click "Cancel"), or error occurs.
|
||||||
* @return False if user calcel the operation or something went wrong, otherwise true.
|
|
||||||
*/
|
*/
|
||||||
bool SaveFileDialog(const FileDialog& params, std::u8string& ret);
|
DialogResult<DialogOutcome<std::u8string>> save_file(const FileDialog& params);
|
||||||
/**
|
/**
|
||||||
* @brief Open the dialog which order user select single directory to open.
|
* @brief Open the dialog which order user select single directory to open.
|
||||||
* @param[in] params The configuration of dialog.
|
* @param[in] params The configuration of dialog.
|
||||||
* @param[out] ret Full path to user selected directory.
|
* @return Full path to user selected directory, or nothing (click "Cancel"), or error occurs.
|
||||||
* @return False if user calcel the operation or something went wrong, otherwise true.
|
|
||||||
*/
|
*/
|
||||||
bool OpenFolderDialog(const FileDialog& params, std::u8string& ret);
|
DialogResult<DialogOutcome<std::u8string>> open_folder(const FileDialog& params);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef NS_YYCC_WINDOWS_COM
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Reference in New Issue
Block a user