feat(interaction): add message boxes and text clipboard
This commit is contained in:
@@ -10,6 +10,7 @@ target_sources(izo
|
||||
src/dialogs.cpp
|
||||
src/directory_watcher.cpp
|
||||
src/time.cpp
|
||||
$<$<PLATFORM_ID:Windows>:src/interaction_windows.cpp>
|
||||
$<$<PLATFORM_ID:Windows>:src/dialogs_windows.cpp>
|
||||
$<$<PLATFORM_ID:Linux>:src/dialogs_linux.cpp>
|
||||
)
|
||||
@@ -27,7 +28,7 @@ set_target_properties(izo PROPERTIES
|
||||
|
||||
if(WIN32)
|
||||
target_compile_definitions(izo PRIVATE UNICODE _UNICODE WIN32_LEAN_AND_MEAN NOMINMAX)
|
||||
target_link_libraries(izo PRIVATE ole32 shell32 uuid)
|
||||
target_link_libraries(izo PRIVATE ole32 shell32 uuid user32)
|
||||
elseif(UNIX AND NOT APPLE)
|
||||
# No link-time desktop dependency. The Linux backend discovers zenity or
|
||||
# kdialog at runtime so applications do not inherit GTK or Qt dependencies.
|
||||
|
||||
120
src/interaction_windows.cpp
Normal file
120
src/interaction_windows.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
#include <izo/clipboard.hpp>
|
||||
#include <izo/message_box.hpp>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace izo {
|
||||
namespace {
|
||||
|
||||
std::wstring widen(std::string_view value) {
|
||||
if (value.empty()) return {};
|
||||
const int count = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, value.data(),
|
||||
static_cast<int>(value.size()), nullptr, 0);
|
||||
if (count <= 0) return {};
|
||||
std::wstring result(static_cast<std::size_t>(count), L'\0');
|
||||
MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, value.data(),
|
||||
static_cast<int>(value.size()), result.data(), count);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string narrow(const wchar_t* value) {
|
||||
if (!value || !*value) return {};
|
||||
const int length = static_cast<int>(wcslen(value));
|
||||
const int count = WideCharToMultiByte(CP_UTF8, 0, value, length, nullptr, 0, nullptr, nullptr);
|
||||
std::string result(static_cast<std::size_t>(count), '\0');
|
||||
WideCharToMultiByte(CP_UTF8, 0, value, length, result.data(), count, nullptr, nullptr);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string windows_error(const char* operation) {
|
||||
return std::string(operation) + " failed with error " + std::to_string(GetLastError());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
message_response show_message_box(const message_box_options& options, std::string* error_message) {
|
||||
UINT flags = MB_TASKMODAL;
|
||||
switch (options.icon) {
|
||||
case message_icon::info: flags |= MB_ICONINFORMATION; break;
|
||||
case message_icon::warning: flags |= MB_ICONWARNING; break;
|
||||
case message_icon::error: flags |= MB_ICONERROR; break;
|
||||
case message_icon::question: flags |= MB_ICONQUESTION; break;
|
||||
}
|
||||
switch (options.buttons) {
|
||||
case message_buttons::ok: flags |= MB_OK; break;
|
||||
case message_buttons::ok_cancel: flags |= MB_OKCANCEL; break;
|
||||
case message_buttons::yes_no: flags |= MB_YESNO; break;
|
||||
case message_buttons::yes_no_cancel: flags |= MB_YESNOCANCEL; break;
|
||||
}
|
||||
const auto title = widen(options.title);
|
||||
const auto message = widen(options.message);
|
||||
switch (MessageBoxW(nullptr, message.c_str(), title.c_str(), flags)) {
|
||||
case IDOK: return message_response::ok;
|
||||
case IDCANCEL: return message_response::cancel;
|
||||
case IDYES: return message_response::yes;
|
||||
case IDNO: return message_response::no;
|
||||
default:
|
||||
if (error_message) *error_message = windows_error("MessageBoxW");
|
||||
return message_response::error;
|
||||
}
|
||||
}
|
||||
|
||||
bool set_clipboard_text(std::string_view text, std::string* error_message) {
|
||||
const auto value = widen(text);
|
||||
if (!OpenClipboard(nullptr)) {
|
||||
if (error_message) *error_message = windows_error("OpenClipboard");
|
||||
return false;
|
||||
}
|
||||
EmptyClipboard();
|
||||
const SIZE_T bytes = (value.size() + 1) * sizeof(wchar_t);
|
||||
HGLOBAL memory = GlobalAlloc(GMEM_MOVEABLE, bytes);
|
||||
if (!memory) {
|
||||
if (error_message) *error_message = windows_error("GlobalAlloc");
|
||||
CloseClipboard();
|
||||
return false;
|
||||
}
|
||||
void* destination = GlobalLock(memory);
|
||||
if (!destination) {
|
||||
if (error_message) *error_message = windows_error("GlobalLock");
|
||||
GlobalFree(memory);
|
||||
CloseClipboard();
|
||||
return false;
|
||||
}
|
||||
memcpy(destination, value.c_str(), bytes);
|
||||
GlobalUnlock(memory);
|
||||
if (!SetClipboardData(CF_UNICODETEXT, memory)) {
|
||||
if (error_message) *error_message = windows_error("SetClipboardData");
|
||||
GlobalFree(memory);
|
||||
CloseClipboard();
|
||||
return false;
|
||||
}
|
||||
CloseClipboard();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<std::string> get_clipboard_text(std::string* error_message) {
|
||||
if (!OpenClipboard(nullptr)) {
|
||||
if (error_message) *error_message = windows_error("OpenClipboard");
|
||||
return std::nullopt;
|
||||
}
|
||||
HANDLE memory = GetClipboardData(CF_UNICODETEXT);
|
||||
if (!memory) {
|
||||
if (error_message) *error_message = "Clipboard does not contain text";
|
||||
CloseClipboard();
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto* value = static_cast<const wchar_t*>(GlobalLock(memory));
|
||||
if (!value) {
|
||||
if (error_message) *error_message = windows_error("GlobalLock");
|
||||
CloseClipboard();
|
||||
return std::nullopt;
|
||||
}
|
||||
auto result = narrow(value);
|
||||
GlobalUnlock(memory);
|
||||
CloseClipboard();
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace izo
|
||||
Reference in New Issue
Block a user