diff --git a/imgui.cpp b/imgui.cpp index b73839d1..21c49f36 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13953,6 +13953,13 @@ void ImGui::ShowMetricsWindow(bool* p_open) TreePop(); } + // Details for TypingSelect + if (TreeNode("TypingSelect", "TypingSelect (%d)", g.TypingSelectState.SearchBuffer[0] != 0 ? 1 : 0)) + { + DebugNodeTypingSelectState(&g.TypingSelectState); + TreePop(); + } + // Details for Docking #ifdef IMGUI_HAS_DOCK if (TreeNode("Docking")) diff --git a/imgui_internal.h b/imgui_internal.h index 710674c2..d2e33e80 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -154,7 +154,7 @@ struct ImGuiTableInstanceData; // Storage for one instance of a same table struct ImGuiTableTempData; // Temporary storage for one table (one per table in the stack), shared between tables. struct ImGuiTableSettings; // Storage for a table .ini settings struct ImGuiTableColumnsSettings; // Storage for a column .ini settings -struct ImGuiTypingSelectData; // Storage for GetTypingSelectRequest() +struct ImGuiTypingSelectState; // Storage for GetTypingSelectRequest() struct ImGuiTypingSelectRequest; // Storage for GetTypingSelectRequest() (aimed to be public) struct ImGuiWindow; // Storage for one window struct ImGuiWindowTempData; // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame, in practice we currently keep it for each window) @@ -1574,20 +1574,22 @@ struct IMGUI_API ImGuiTypingSelectRequest int SearchBufferLen; const char* SearchBuffer; bool SelectRequest; // Set when buffer was modified this frame, requesting a selection. - bool SingleCharMode; // Notify when buffer contains same character repeated, to implement special mode. + bool SingleCharMode; // Notify when buffer contains same character repeated, to implement special mode. In this situation it preferred to not display any on-screen search indication. ImS8 SingleCharSize; // Length in bytes of first letter codepoint (1 for ascii, 2-4 for UTF-8). If (SearchBufferLen==RepeatCharSize) only 1 letter has been input. }; // Storage for GetTypingSelectRequest() -struct IMGUI_API ImGuiTypingSelectData +struct IMGUI_API ImGuiTypingSelectState { ImGuiTypingSelectRequest Request; // User-facing data char SearchBuffer[64]; // Search buffer: no need to make dynamic as this search is very transient. ImGuiID FocusScope; int LastRequestFrame = 0; float LastRequestTime = 0.0f; + bool SingleCharModeLock = false; // After a certain single char repeat count we lock into SingleCharMode. Two benefits: 1) buffer never fill, 2) we can provide an immediate SingleChar mode without timer elapsing. - ImGuiTypingSelectData() { memset(this, 0, sizeof(*this)); } + ImGuiTypingSelectState() { memset(this, 0, sizeof(*this)); } + void Clear() { SearchBuffer[0] = 0; SingleCharModeLock = false; } // We preserve remaining data for easier debugging }; //----------------------------------------------------------------------------- @@ -2082,7 +2084,7 @@ struct ImGuiContext short TooltipOverrideCount; ImVector ClipboardHandlerData; // If no custom clipboard handler is defined ImVector MenusIdSubmittedThisFrame; // A list of menu IDs that were rendered at least once - ImGuiTypingSelectData TypingSelectData; + ImGuiTypingSelectState TypingSelectState; // State for GetTypingSelectRequest() // Platform support ImGuiPlatformImeData PlatformImeData; // Data updated by current frame @@ -3124,7 +3126,7 @@ namespace ImGui // Typing-Select API IMGUI_API ImGuiTypingSelectRequest* GetTypingSelectRequest(ImGuiTypingSelectFlags flags = ImGuiTypingSelectFlags_None); - IMGUI_API int TypingSelectFindResult(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx); + IMGUI_API int TypingSelectFindTargetIndex(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx); // Internal Columns API (this is not exposed because we will encourage transitioning to the Tables API) IMGUI_API void SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect); @@ -3337,6 +3339,7 @@ namespace ImGui IMGUI_API void DebugNodeTable(ImGuiTable* table); IMGUI_API void DebugNodeTableSettings(ImGuiTableSettings* settings); IMGUI_API void DebugNodeInputTextState(ImGuiInputTextState* state); + IMGUI_API void DebugNodeTypingSelectState(ImGuiTypingSelectState* state); IMGUI_API void DebugNodeWindow(ImGuiWindow* window, const char* label); IMGUI_API void DebugNodeWindowSettings(ImGuiWindowSettings* settings); IMGUI_API void DebugNodeWindowsList(ImVector* windows, const char* label); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 5c4dc2a1..c92a243d 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6619,13 +6619,14 @@ bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags ImGuiTypingSelectRequest* ImGui::GetTypingSelectRequest(ImGuiTypingSelectFlags flags) { ImGuiContext& g = *GImGui; - ImGuiTypingSelectData* data = &g.TypingSelectData; + ImGuiTypingSelectState* data = &g.TypingSelectState; ImGuiTypingSelectRequest* out_request = &data->Request; // Clear buffer + const float TYPING_SELECT_RESET_TIMER = 1.80f; // FIXME: Potentially move to IO config. + const int TYPING_SELECT_SINGLE_CHAR_COUNT_FOR_LOCK = 4; // Lock single char matching when repeating same char 4 times if (data->SearchBuffer[0] != 0) { - const float TYPING_SELECT_RESET_TIMER = 1.70f; // FIXME: Potentially move to IO config. bool clear_buffer = false; clear_buffer |= (g.NavFocusScopeId != data->FocusScope); clear_buffer |= (data->LastRequestTime + TYPING_SELECT_RESET_TIMER < g.Time); @@ -6635,42 +6636,53 @@ ImGuiTypingSelectRequest* ImGui::GetTypingSelectRequest(ImGuiTypingSelectFlags f clear_buffer |= IsKeyPressed(ImGuiKey_Backspace) && (flags & ImGuiTypingSelectFlags_AllowBackspace) == 0; //if (clear_buffer) { IMGUI_DEBUG_LOG("GetTypingSelectRequest(): Clear SearchBuffer.\n"); } if (clear_buffer) - data->SearchBuffer[0] = 0; + data->Clear(); } // Append to buffer const int buffer_max_len = IM_ARRAYSIZE(data->SearchBuffer) - 1; int buffer_len = (int)strlen(data->SearchBuffer); - bool buffer_changed = false; + bool select_request = false; for (ImWchar w : g.IO.InputQueueCharacters) { - if (w < 32 || (buffer_len == 0 && ImCharIsBlankW(w))) // Ignore leading blanks + const int w_len = ImTextCountUtf8BytesFromStr(&w, &w + 1); + if (w < 32 || (buffer_len == 0 && ImCharIsBlankW(w)) || (buffer_len + w_len > buffer_max_len)) // Ignore leading blanks continue; - int utf8_len = ImTextCountUtf8BytesFromStr(&w, &w + 1); - if (buffer_len + utf8_len > buffer_max_len) - break; - ImTextCharToUtf8(data->SearchBuffer + buffer_len, (unsigned int)w); - buffer_len += utf8_len; - buffer_changed = true; + char w_buf[5]; + ImTextCharToUtf8(w_buf, (unsigned int)w); + if (data->SingleCharModeLock && w_len == out_request->SingleCharSize && memcmp(w_buf, data->SearchBuffer, w_len) == 0) + { + select_request = true; // Same character: don't need to append to buffer. + continue; + } + if (data->SingleCharModeLock) + { + data->Clear(); // Different character: clear + buffer_len = 0; + } + memcpy(data->SearchBuffer + buffer_len, w_buf, w_len + 1); // Append + buffer_len += w_len; + select_request = true; } g.IO.InputQueueCharacters.resize(0); + + // Handle backspace if ((flags & ImGuiTypingSelectFlags_AllowBackspace) && IsKeyPressed(ImGuiKey_Backspace, 0, ImGuiInputFlags_Repeat)) { char* p = (char*)(void*)ImTextFindPreviousUtf8Codepoint(data->SearchBuffer, data->SearchBuffer + buffer_len); *p = 0; buffer_len = (int)(p - data->SearchBuffer); } + + // Return request if any if (buffer_len == 0) return NULL; - - if (buffer_changed) + if (select_request) { data->FocusScope = g.NavFocusScopeId; data->LastRequestFrame = g.FrameCount; data->LastRequestTime = (float)g.Time; } - - // Return request if any out_request->Flags = flags; out_request->SearchBufferLen = buffer_len; out_request->SearchBuffer = data->SearchBuffer; @@ -6691,8 +6703,10 @@ ImGuiTypingSelectRequest* ImGui::GetTypingSelectRequest(ImGuiTypingSelectFlags f for (; p < buf_end; p += c0_len) if (memcmp(buf_begin, p, (size_t)c0_len) != 0) break; - out_request->SingleCharMode = (p == buf_end); - out_request->SingleCharSize = out_request->SingleCharMode ? (ImS8)c0_len : 0; + const int single_char_count = (p == buf_end) ? (out_request->SearchBufferLen / c0_len) : 0; + out_request->SingleCharMode = (single_char_count > 0 || data->SingleCharModeLock); + out_request->SingleCharSize = (ImS8)c0_len; + data->SingleCharModeLock |= (single_char_count >= TYPING_SELECT_SINGLE_CHAR_COUNT_FOR_LOCK); // From now on we stop search matching to lock to single char mode. } return out_request; @@ -6708,16 +6722,23 @@ static int ImStrimatchlen(const char* s1, const char* s1_end, const char* s2) // Default handler for finding a result for typing-select. You may implement your own. // You might want to display a tooltip to visualize the current request. -// With same single character mode enabled: -// - it may make less sense to be displaying a tooltip. +// When SingleCharMode is set: +// - it is better to NOT display a tooltip of other on-screen display indicator. // - the index of the currently focused item is required. -// - in the context of using BeginMultiSelect(), you may retrieve data you stored to ImGuiMultiSelectIO::NavIdItem and convert it to an index. -int ImGui::TypingSelectFindResult(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx) +// if your SetNextItemSelectionData() values are index, you can obtain it from ImGuiMultiSelectIO::NavIdItem, otherwise from g.NavLastValidSelectionUserData. +int ImGui::TypingSelectFindTargetIndex(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx) { if (req->SelectRequest == false) return -1; + + ImGuiContext& g = *GImGui; + g.NavDisableMouseHover = true; if (req->SingleCharMode && (req->Flags & ImGuiTypingSelectFlags_AllowSingleCharMode)) { + // FIXME: Assume selection user data is index. Would be extremely practical. + //if (nav_item_idx == -1) + // nav_item_idx = (int)g.NavLastValidSelectionUserData; + // Special handling when a same character is typed twice in a row : perform search on a single letter and goes to next. int first_match_idx = -1; bool return_next_match = false; @@ -6755,6 +6776,17 @@ int ImGui::TypingSelectFindResult(ImGuiTypingSelectRequest* req, int items_count return longest_match_idx; } +void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data) +{ +#ifndef IMGUI_DISABLE_DEBUG_TOOLS + Text("SearchBuffer = \"%s\"", data->SearchBuffer); + Text("SingleCharMode = %d, Size = %d, Lock = %d", data->Request.SingleCharMode, data->Request.SingleCharSize, data->SingleCharModeLock); + Text("LastRequest = time: %.2f, frame: %d", data->LastRequestTime, data->LastRequestFrame); +#else + IM_UNUSED(storage); +#endif +} + //------------------------------------------------------------------------- // [SECTION] Widgets: Multi-Select support