feat(interaction): add message boxes and text clipboard

This commit is contained in:
2026-06-18 19:23:33 -05:00
parent 7cfdc13795
commit 6fe42963fb
2 changed files with 122 additions and 1 deletions

View File

@@ -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
View 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