From fc9ccad6b96de70219a644395b6a2bd069e61656 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 20 Aug 2020 11:25:39 +0200 Subject: [PATCH] InputText: Add ImGuiInputTextFlags_CallbackEdit, selection helpers in ImGuiInputTextCallbackData(). Add simple InputText() callbacks demo. --- docs/CHANGELOG.txt | 5 ++++ imgui.h | 8 ++++-- imgui_demo.cpp | 61 +++++++++++++++++++++++++++++++++++++++++++++- imgui_internal.h | 1 + imgui_widgets.cpp | 11 ++++++++- 5 files changed, 82 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 83ac4df7f..5479abb6c 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -37,8 +37,13 @@ HOW TO UPDATE? Other Changes: +- InputText: Added selection helpers in ImGuiInputTextCallbackData(). +- InputText: Added ImGuiInputTextFlags_CallbackEdit to modify internally owned buffer after an edit. + (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the + underlying buffer while focus is active). - DragFloat, DragScalar: Fixed ImGuiSliderFlags_ClampOnInput not being honored in the special case where v_min == v_max. (#3361) +- Demo: Add simple InputText() callbacks demo (aside from the more elaborate ones in 'Examples->Console'). ----------------------------------------------------------------------- diff --git a/imgui.h b/imgui.h index d661ccfe8..070582785 100644 --- a/imgui.h +++ b/imgui.h @@ -854,6 +854,7 @@ enum ImGuiInputTextFlags_ ImGuiInputTextFlags_NoUndoRedo = 1 << 16, // Disable undo/redo. Note that input text owns the text data while active, if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID(). ImGuiInputTextFlags_CharsScientific = 1 << 17, // Allow 0123456789.+-*/eE (Scientific notation input) ImGuiInputTextFlags_CallbackResize = 1 << 18, // Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this) + ImGuiInputTextFlags_CallbackEdit = 1 << 19, // Callback on any edit (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active) // [Internal] ImGuiInputTextFlags_Multiline = 1 << 20, // For internal use by InputTextMultiline() ImGuiInputTextFlags_NoMarkEdited = 1 << 21 // For internal use by functions using InputText() before reformatting data @@ -1634,9 +1635,10 @@ struct ImGuiIO // Shared state of InputText(), passed as an argument to your callback when a ImGuiInputTextFlags_Callback* flag is used. // The callback function should return 0 by default. // Callbacks (follow a flag name and see comments in ImGuiInputTextFlags_ declarations for more details) +// - ImGuiInputTextFlags_CallbackEdit: Callback on buffer edit (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active) +// - ImGuiInputTextFlags_CallbackAlways: Callback on each iteration // - ImGuiInputTextFlags_CallbackCompletion: Callback on pressing TAB // - ImGuiInputTextFlags_CallbackHistory: Callback on pressing Up/Down arrows -// - ImGuiInputTextFlags_CallbackAlways: Callback on each iteration // - ImGuiInputTextFlags_CallbackCharFilter: Callback on character inputs to replace or discard them. Modify 'EventChar' to replace or discard, or return 1 in callback to discard. // - ImGuiInputTextFlags_CallbackResize: Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. struct ImGuiInputTextCallbackData @@ -1663,7 +1665,9 @@ struct ImGuiInputTextCallbackData IMGUI_API ImGuiInputTextCallbackData(); IMGUI_API void DeleteChars(int pos, int bytes_count); IMGUI_API void InsertChars(int pos, const char* text, const char* text_end = NULL); - bool HasSelection() const { return SelectionStart != SelectionEnd; } + void SelectAll() { SelectionStart = 0; SelectionEnd = BufTextLen; } + void ClearSelection() { SelectionStart = SelectionEnd = BufTextLen; } + bool HasSelection() const { return SelectionStart != SelectionEnd; } }; // Resizing callback data to apply custom constraint. As enabled by SetNextWindowSizeConstraints(). Callback is called during the next Begin(). diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 77a7d8705..f9d1a9eb2 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1177,8 +1177,11 @@ static void ShowDemoWindowWidgets() static char buf4[64] = ""; ImGui::InputText("uppercase", buf4, 64, ImGuiInputTextFlags_CharsUppercase); static char buf5[64] = ""; ImGui::InputText("no blank", buf5, 64, ImGuiInputTextFlags_CharsNoBlank); static char buf6[64] = ""; ImGui::InputText("\"imgui\" letters", buf6, 64, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); + ImGui::TreePop(); + } - ImGui::Text("Password input"); + if (ImGui::TreeNode("Password Input")) + { static char password[64] = "password123"; ImGui::InputText("password", password, IM_ARRAYSIZE(password), ImGuiInputTextFlags_Password); ImGui::SameLine(); HelpMarker("Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n"); @@ -1187,6 +1190,62 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + if (ImGui::TreeNode("Completion, History, Edit Callbacks")) + { + struct Funcs + { + static int MyCallback(ImGuiInputTextCallbackData* data) + { + if (data->EventFlag == ImGuiInputTextFlags_CallbackCompletion) + { + data->InsertChars(data->CursorPos, ".."); + } + else if (data->EventFlag == ImGuiInputTextFlags_CallbackHistory) + { + if (data->EventKey == ImGuiKey_UpArrow) + { + data->DeleteChars(0, data->BufTextLen); + data->InsertChars(0, "Pressed Up!"); + data->SelectAll(); + } + else if (data->EventKey == ImGuiKey_DownArrow) + { + data->DeleteChars(0, data->BufTextLen); + data->InsertChars(0, "Pressed Down!"); + data->SelectAll(); + } + } + else if (data->EventFlag == ImGuiInputTextFlags_CallbackEdit) + { + // Toggle casing of first character + char c = data->Buf[0]; + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) data->Buf[0] ^= 32; + data->BufDirty = true; + + // Increment a counter + int* p_int = (int*)data->UserData; + *p_int = *p_int + 1; + } + return 0; + } + }; + static char buf1[64]; + ImGui::InputText("Completion", buf1, 64, ImGuiInputTextFlags_CallbackCompletion, Funcs::MyCallback); + ImGui::SameLine(); HelpMarker("Here we append \"..\" each time Tab is pressed. See 'Examples>Console' for a more meaningful demonstration of using this callback."); + + static char buf2[64]; + ImGui::InputText("History", buf2, 64, ImGuiInputTextFlags_CallbackHistory, Funcs::MyCallback); + ImGui::SameLine(); HelpMarker("Here we replace and select text each time Up/Down are pressed. See 'Examples>Console' for a more meaningful demonstration of using this callback."); + + static char buf3[64]; + static int edit_count = 0; + ImGui::InputText("Edit", buf3, 64, ImGuiInputTextFlags_CallbackEdit, Funcs::MyCallback, (void*)&edit_count); + ImGui::SameLine(); HelpMarker("Here we toggle the casing of the first character on every edits + count edits."); + ImGui::SameLine(); ImGui::Text("(%d)", edit_count); + + ImGui::TreePop(); + } + if (ImGui::TreeNode("Resize Callback")) { // To wire InputText() with std::string or any other custom string type, diff --git a/imgui_internal.h b/imgui_internal.h index 1e4384d0c..582244444 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -858,6 +858,7 @@ struct IMGUI_API ImGuiInputTextState float CursorAnim; // timer for cursor blink, reset on every user action so the cursor reappears immediately bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!) bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection + bool Edited; // edited this frame ImGuiInputTextFlags UserFlags; // Temporarily set while we call user's callback ImGuiInputTextCallback UserCallback; // " void* UserCallbackData; // " diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 188bb882a..eee4d0f30 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3514,6 +3514,7 @@ static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) ImWchar* dst = obj->TextW.Data + pos; // We maintain our buffer length in both UTF-8 and wchar formats + obj->Edited = true; obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n); obj->CurLenW -= n; @@ -3548,6 +3549,7 @@ static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const Im memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar)); memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar)); + obj->Edited = true; obj->CurLenW += new_text_len; obj->CurLenA += new_text_len_utf8; obj->TextW[obj->CurLenW] = '\0'; @@ -3946,6 +3948,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { IM_ASSERT(state != NULL); backup_current_text_length = state->CurLenA; + state->Edited = false; state->BufCapacityA = buf_size; state->UserFlags = flags; state->UserCallback = callback; @@ -4183,7 +4186,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } // User callback - if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0) + if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackEdit | ImGuiInputTextFlags_CallbackAlways)) != 0) { IM_ASSERT(callback != NULL); @@ -4205,8 +4208,14 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ event_flag = ImGuiInputTextFlags_CallbackHistory; event_key = ImGuiKey_DownArrow; } + else if ((flags & ImGuiInputTextFlags_CallbackEdit) && state->Edited) + { + event_flag = ImGuiInputTextFlags_CallbackEdit; + } else if (flags & ImGuiInputTextFlags_CallbackAlways) + { event_flag = ImGuiInputTextFlags_CallbackAlways; + } if (event_flag) {