diff --git a/docs/changelog.txt b/docs/changelog.txt index 14efa5056..8971dd0ac 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -39,6 +39,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - Fix extra keys appearing in DFHack text boxes when shift (or any other modifier) is released before the other key you were pressing ## Misc Improvements +- ``widgets.EditField``: DFHack edit fields now support cut/copy/paste with the system clipboard with Ctrl-X/Ctrl-C/Ctrl-V ## Documentation diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 0554629a9..785e477dd 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -2832,6 +2832,13 @@ and are only documented here for completeness: Returns 0 if the address is not found. Requires a heap snapshot. +* ``dfhack.internal.getClipboardTextCp437()`` + + Gets the system clipboard text (and converts text to CP437 encoding). + +* ``dfhack.internal.setClipboardTextCp437(text)`` + + Sets the system clipboard text from a CP437 string. .. _lua-core-context: @@ -4705,6 +4712,12 @@ following keyboard hotkeys: - Ctrl-B/Ctrl-F: move the cursor one word back or forward. - Ctrl-A/Ctrl-E: move the cursor to the beginning/end of the text. +The widget also supports integration with the system clipboard: + +- Ctrl-C: copy current text to the system clipboard +- Ctrl-X: copy current text to the system clipboard and clear text in widget +- Ctrl-V: paste text from the system clipboard (text is converted to cp437) + The ``EditField`` class also provides the following functions: * ``editfield:setCursor([cursor_pos])`` diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 8ed9af079..7fb14baeb 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -3039,6 +3039,8 @@ static const LuaWrapper::FunctionReg dfhack_internal_module[] = { WRAPN(getAddressSizeInHeap, get_address_size_in_heap), WRAPN(getRootAddressOfHeapObject, get_root_address_of_heap_object), WRAPN(msizeAddress, msize_address), + WRAP(getClipboardTextCp437), + WRAP(setClipboardTextCp437), { NULL, NULL } }; diff --git a/library/MiscUtils.cpp b/library/MiscUtils.cpp index b959e756e..7ffd4e1c6 100644 --- a/library/MiscUtils.cpp +++ b/library/MiscUtils.cpp @@ -27,6 +27,8 @@ distribution. #include "MiscUtils.h" #include "ColorText.h" +#include "modules/DFSDL.h" + #ifndef LINUX_BUILD // We don't want min and max macros #define NOMINMAX diff --git a/library/include/modules/DFSDL.h b/library/include/modules/DFSDL.h index 626224d60..36dd641d5 100644 --- a/library/include/modules/DFSDL.h +++ b/library/include/modules/DFSDL.h @@ -47,7 +47,17 @@ DFHACK_EXPORT void DFSDL_FreeSurface(SDL_Surface *surface); // DFHACK_EXPORT int DFSDL_SemWait(SDL_sem *sem); // DFHACK_EXPORT int DFSDL_SemPost(SDL_sem *sem); DFHACK_EXPORT int DFSDL_PushEvent(SDL_Event *event); +DFHACK_EXPORT void DFSDL_free(void *ptr); + +// submitted and returned text is UTF-8 +// see wrapper functions below for cp-437 variants +DFHACK_EXPORT char * DFSDL_GetClipboardText(); +DFHACK_EXPORT int DFSDL_SetClipboardText(const char *text); } +// System clipboard -- submitted and returned text must be in CP437 +DFHACK_EXPORT std::string getClipboardTextCp437(); +DFHACK_EXPORT bool setClipboardTextCp437(std::string text); + } diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 7a3201f28..d6dcafd63 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -640,8 +640,12 @@ function EditField:setCursor(cursor) end function EditField:setText(text, cursor) + local old = self.text self.text = text self:setCursor(cursor) + if self.on_change and text ~= old then + self.on_change(self.text, old) + end end function EditField:postUpdateLayout() @@ -699,11 +703,7 @@ function EditField:onInput(keys) end if self.key and (keys.LEAVESCREEN or keys._MOUSE_R_DOWN) then - local old = self.text self:setText(self.saved_text) - if self.on_change and old ~= self.saved_text then - self.on_change(self.text, old) - end self:setFocus(false) return true end @@ -747,9 +747,6 @@ function EditField:onInput(keys) return self.modal end end - if self.on_change and self.text ~= old then - self.on_change(self.text, old) - end return true elseif keys.KEYBOARD_CURSOR_LEFT then self:setCursor(self.cursor - 1) @@ -772,6 +769,16 @@ function EditField:onInput(keys) elseif keys.CUSTOM_CTRL_E then -- end self:setCursor() return true + elseif keys.CUSTOM_CTRL_C then + dfhack.internal.setClipboardTextCp437(self.text) + return true + elseif keys.CUSTOM_CTRL_X then + dfhack.internal.setClipboardTextCp437(self.text) + self:setText('') + return true + elseif keys.CUSTOM_CTRL_V then + self:insert(dfhack.internal.getClipboardTextCp437()) + return true end -- if we're modal, then unconditionally eat all the input diff --git a/library/modules/DFSDL.cpp b/library/modules/DFSDL.cpp index 7aa7f36d5..aa54cf66c 100644 --- a/library/modules/DFSDL.cpp +++ b/library/modules/DFSDL.cpp @@ -5,6 +5,8 @@ #include "Debug.h" #include "PluginManager.h" +#include + namespace DFHack { DBG_DECLARE(core, dfsdl, DebugCategory::LINFO); } @@ -35,6 +37,10 @@ void (*g_SDL_FreeSurface)(SDL_Surface *) = nullptr; // int (*g_SDL_SemWait)(DFSDL_sem *) = nullptr; // int (*g_SDL_SemPost)(DFSDL_sem *) = nullptr; int (*g_SDL_PushEvent)(SDL_Event *) = nullptr; +SDL_bool (*g_SDL_HasClipboardText)(); +int (*g_SDL_SetClipboardText)(const char *text); +char * (*g_SDL_GetClipboardText)(); +void (*g_SDL_free)(void *); bool DFSDL::init(color_ostream &out) { for (auto &lib_str : SDL_LIBS) { @@ -71,6 +77,10 @@ bool DFSDL::init(color_ostream &out) { // bind(g_sdl_handle, SDL_SemWait); // bind(g_sdl_handle, SDL_SemPost); bind(g_sdl_handle, SDL_PushEvent); + bind(g_sdl_handle, SDL_HasClipboardText); + bind(g_sdl_handle, SDL_SetClipboardText); + bind(g_sdl_handle, SDL_GetClipboardText); + bind(g_sdl_handle, SDL_free); #undef bind DEBUG(dfsdl,out).print("sdl successfully loaded\n"); @@ -124,3 +134,30 @@ void DFSDL::DFSDL_FreeSurface(SDL_Surface *surface) { int DFSDL::DFSDL_PushEvent(SDL_Event *event) { return g_SDL_PushEvent(event); } + +void DFSDL::DFSDL_free(void *ptr) { + g_SDL_free(ptr); +} + +char * DFSDL::DFSDL_GetClipboardText() { + return g_SDL_GetClipboardText(); +} + +int DFSDL::DFSDL_SetClipboardText(const char *text) { + return g_SDL_SetClipboardText(text); +} + +DFHACK_EXPORT std::string DFHack::getClipboardTextCp437() { + if (!g_sdl_handle || g_SDL_HasClipboardText() != SDL_TRUE) + return ""; + char *text = g_SDL_GetClipboardText(); + std::string textcp437 = UTF2DF(text); + DFHack::DFSDL::DFSDL_free(text); + return textcp437; +} + +DFHACK_EXPORT bool DFHack::setClipboardTextCp437(std::string text) { + if (!g_sdl_handle) + return false; + return 0 == DFHack::DFSDL::DFSDL_SetClipboardText(DF2UTF(text).c_str()); +}