From 9473cd491e57d4644fcdd4eb2aa6b799e37e80fd Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 11 Feb 2015 17:39:13 +0000 Subject: [PATCH] InputText(): added ImGuiInputTextFlags_CallbackCharFilter system for filtering/replacement. Callback now passed an "EventFlag" parameter. --- imgui.cpp | 92 ++++++++++++++++++++++++++++++++++++++++++++----------- imgui.h | 30 +++++++++++------- 2 files changed, 93 insertions(+), 29 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 59de21d6..cb994ecb 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -129,6 +129,7 @@ Occasionally introducing changes that are breaking the API. The breakage are generally minor and easy to fix. Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code. + - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now. - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing() - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused) @@ -5223,27 +5224,44 @@ void ImGuiTextEditCallbackData::InsertChars(int pos, const char* new_text, const SelectionStart = SelectionEnd = CursorPos; } -static bool InputTextFilterCharacter(ImWchar c, ImGuiInputTextFlags flags) +// Return false to discard a character. +static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) { + unsigned int c = *p_char; + if (c < 128 && c != ' ' && !isprint((int)(c & 0xFF))) - return true; + return false; if (c >= 0xE000 && c <= 0xF8FF) // Filter private Unicode range. I don't imagine anybody would want to input them. GLFW on OSX seems to send private characters for special keys like arrow keys. - return true; + return false; if (flags & ImGuiInputTextFlags_CharsDecimal) if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/')) - return true; + return false; if (flags & ImGuiInputTextFlags_CharsHexadecimal) if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F')) - return true; + return false; - return false; + if (flags & ImGuiInputTextFlags_CallbackCharFilter) + { + ImGuiTextEditCallbackData callback_data; + memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData)); + callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter; + callback_data.EventChar = c; + callback_data.Flags = flags; + callback_data.UserData = user_data; + callback(&callback_data); + *p_char = callback_data.EventChar; + if (!callback_data.EventChar) + return false; + } + + return true; } // Edit a string of text -bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, void (*callback)(ImGuiTextEditCallbackData*), void* user_data) +bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) { ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); @@ -5344,13 +5362,13 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT // Process text input (before we check for Return because using some IME will effectively send a Return?) for (int n = 0; n < IM_ARRAYSIZE(g.IO.InputCharacters) && g.IO.InputCharacters[n]; n++) { - const ImWchar c = g.IO.InputCharacters[n]; + unsigned int c = (unsigned int)g.IO.InputCharacters[n]; if (c) { // Insert character if they pass filtering - if (InputTextFilterCharacter(c, flags)) + if (!InputTextFilterCharacter(&c, flags, callback, user_data)) continue; - edit_state.OnKeyPressed(c); + edit_state.OnKeyPressed((int)c); } } @@ -5407,7 +5425,7 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT break; if (c >= 0x10000) continue; - if (InputTextFilterCharacter((ImWchar)c, flags)) + if (!InputTextFilterCharacter(&c, flags, callback, user_data)) continue; clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c; } @@ -5442,17 +5460,28 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT IM_ASSERT(callback != NULL); // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment. + ImGuiInputTextFlags event_flag = 0; ImGuiKey event_key = ImGuiKey_COUNT; if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab)) + { + event_flag = ImGuiInputTextFlags_CallbackCompletion; event_key = ImGuiKey_Tab; + } else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow)) + { + event_flag = ImGuiInputTextFlags_CallbackHistory; event_key = ImGuiKey_UpArrow; + } else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow)) + { + event_flag = ImGuiInputTextFlags_CallbackHistory; event_key = ImGuiKey_DownArrow; + } if (event_key != ImGuiKey_COUNT || (flags & ImGuiInputTextFlags_CallbackAlways) != 0) { ImGuiTextEditCallbackData callback_data; + callback_data.EventFlag = event_flag; callback_data.EventKey = event_key; callback_data.Buf = text_tmp_utf8; callback_data.BufSize = edit_state.BufSize; @@ -8313,6 +8342,33 @@ void ImGui::ShowTestWindow(bool* opened) ImGui::TreePop(); } + if (ImGui::TreeNode("Filtered Text Input")) + { + static char buf1[64] = ""; ImGui::InputText("default", buf1, 64); + static char buf2[64] = ""; ImGui::InputText("decimal", buf2, 64, ImGuiInputTextFlags_CharsDecimal); + static char buf3[64] = ""; ImGui::InputText("hexadecimal", buf3, 64, ImGuiInputTextFlags_CharsHexadecimal); + struct TextFilters + { + static int FilterAZ(ImGuiTextEditCallbackData* data) + { + const ImWchar c = data->EventChar; + if (!((c >= 'a' && c <= 'z') || c >= 'A' && c <= 'Z')) + data->EventChar = 0; + return 0; + } + static int FilterUppercase(ImGuiTextEditCallbackData* data) + { + const ImWchar c = data->EventChar; + if (c >= 'a' && c <= 'z') + data->EventChar += 'A'-'a'; + return 0; + } + }; + static char buf4[64] = ""; ImGui::InputText("a-z only", buf4, 64, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterAZ); + static char buf5[64] = ""; ImGui::InputText("uppercase", buf5, 64, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterUppercase); + ImGui::TreePop(); + } + static bool check = true; ImGui::Checkbox("checkbox", &check); @@ -8967,18 +9023,18 @@ struct ExampleAppConsole } } - static void TextEditCallbackStub(ImGuiTextEditCallbackData* data) + static int TextEditCallbackStub(ImGuiTextEditCallbackData* data) { ExampleAppConsole* console = (ExampleAppConsole*)data->UserData; - console->TextEditCallback(data); + return console->TextEditCallback(data); } - void TextEditCallback(ImGuiTextEditCallbackData* data) + int TextEditCallback(ImGuiTextEditCallbackData* data) { //AddLog("cursor: %d, selection: %d-%d", data->CursorPos, data->SelectionStart, data->SelectionEnd); - switch (data->EventKey) + switch (data->EventFlag) { - case ImGuiKey_Tab: + case ImGuiInputTextFlags_CallbackCompletion: { // Example of TEXT COMPLETION @@ -9043,8 +9099,7 @@ struct ExampleAppConsole break; } - case ImGuiKey_UpArrow: - case ImGuiKey_DownArrow: + case ImGuiInputTextFlags_CallbackHistory: { // Example of HISTORY const int prev_history_pos = HistoryPos; @@ -9071,6 +9126,7 @@ struct ExampleAppConsole } } } + return 0; } }; diff --git a/imgui.h b/imgui.h index 95f19b2e..ef592a03 100644 --- a/imgui.h +++ b/imgui.h @@ -44,6 +44,7 @@ typedef int ImGuiWindowFlags; // enum ImGuiWindowFlags_ typedef int ImGuiSetCondition; // enum ImGuiSetCondition_ typedef int ImGuiInputTextFlags; // enum ImGuiInputTextFlags_ struct ImGuiTextEditCallbackData; // for advanced uses of InputText() +typedef int (*ImGuiTextEditCallback)(ImGuiTextEditCallbackData *data); struct ImVec2 { @@ -275,7 +276,7 @@ namespace ImGui IMGUI_API bool CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value); IMGUI_API bool RadioButton(const char* label, bool active); IMGUI_API bool RadioButton(const char* label, int* v, int v_button); - IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, void (*callback)(ImGuiTextEditCallbackData*) = NULL, void* user_data = NULL); + IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiTextEditCallback callback = NULL, void* user_data = NULL); IMGUI_API bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); IMGUI_API bool InputFloat2(const char* label, float v[2], int decimal_precision = -1); IMGUI_API bool InputFloat3(const char* label, float v[3], int decimal_precision = -1); @@ -391,7 +392,8 @@ enum ImGuiInputTextFlags_ ImGuiInputTextFlags_EnterReturnsTrue = 1 << 3, // Return 'true' when Enter is pressed (as opposed to when the value was modified) ImGuiInputTextFlags_CallbackCompletion = 1 << 4, // Call user function on pressing TAB (for completion handling) ImGuiInputTextFlags_CallbackHistory = 1 << 5, // Call user function on pressing Up/Down arrows (for history handling) - ImGuiInputTextFlags_CallbackAlways = 1 << 6 // Call user function every time + ImGuiInputTextFlags_CallbackAlways = 1 << 6, // Call user function every time + ImGuiInputTextFlags_CallbackCharFilter = 1 << 7 // Call user function to filter character. Modify data->EventChar to replace/filter input. //ImGuiInputTextFlags_AlignCenter = 1 << 6, }; @@ -716,15 +718,21 @@ struct ImGuiStorage // Shared state of InputText(), passed to callback when a ImGuiInputTextFlags_Callback* flag is used. struct ImGuiTextEditCallbackData { - ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only - char* Buf; // Current text // Read-write (pointed data only) - size_t BufSize; // // Read-only - bool BufDirty; // Set if you modify Buf directly // Write - ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only - int CursorPos; // // Read-write - int SelectionStart; // // Read-write (== to SelectionEnd when no selection) - int SelectionEnd; // // Read-write - void* UserData; // What user passed to InputText() + ImGuiInputTextFlags EventFlag; // One of ImGuiInputTextFlags_Callback* // Read-only + ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only + void* UserData; // What user passed to InputText() // Read-only + + // CharFilter event: + ImWchar EventChar; // Character input // Read-write (replace character or set to zero) + + // Completion,History,Always events: + ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only + char* Buf; // Current text // Read-write (pointed data only) + size_t BufSize; // // Read-only + bool BufDirty; // Set if you modify Buf directly // Write + int CursorPos; // // Read-write + int SelectionStart; // // Read-write (== to SelectionEnd when no selection) + int SelectionEnd; // // Read-write // NB: calling those function loses selection. void DeleteChars(int pos, int bytes_count);