Browse Source

TypingSelect: fast switch between characters in + debug, internal renames.

# Conflicts:
#	imgui.cpp
#	imgui_internal.h
ocornut 2 years ago
parent
commit
661a70fc79
3 changed files with 69 additions and 27 deletions
  1. 7 0
      imgui.cpp
  2. 9 6
      imgui_internal.h
  3. 53 21
      imgui_widgets.cpp

+ 7 - 0
imgui.cpp

@@ -13953,6 +13953,13 @@ void ImGui::ShowMetricsWindow(bool* p_open)
         TreePop();
         TreePop();
     }
     }
 
 
+    // Details for TypingSelect
+    if (TreeNode("TypingSelect", "TypingSelect (%d)", g.TypingSelectState.SearchBuffer[0] != 0 ? 1 : 0))
+    {
+        DebugNodeTypingSelectState(&g.TypingSelectState);
+        TreePop();
+    }
+
     // Details for Docking
     // Details for Docking
 #ifdef IMGUI_HAS_DOCK
 #ifdef IMGUI_HAS_DOCK
     if (TreeNode("Docking"))
     if (TreeNode("Docking"))

+ 9 - 6
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 ImGuiTableTempData;          // Temporary storage for one table (one per table in the stack), shared between tables.
 struct ImGuiTableSettings;          // Storage for a table .ini settings
 struct ImGuiTableSettings;          // Storage for a table .ini settings
 struct ImGuiTableColumnsSettings;   // Storage for a column .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 ImGuiTypingSelectRequest;    // Storage for GetTypingSelectRequest() (aimed to be public)
 struct ImGuiWindow;                 // Storage for one window
 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)
 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;
     int                     SearchBufferLen;
     const char*             SearchBuffer;
     const char*             SearchBuffer;
     bool                    SelectRequest;      // Set when buffer was modified this frame, requesting a selection.
     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.
     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()
 // Storage for GetTypingSelectRequest()
-struct IMGUI_API ImGuiTypingSelectData
+struct IMGUI_API ImGuiTypingSelectState
 {
 {
     ImGuiTypingSelectRequest Request;           // User-facing data
     ImGuiTypingSelectRequest Request;           // User-facing data
     char            SearchBuffer[64];           // Search buffer: no need to make dynamic as this search is very transient.
     char            SearchBuffer[64];           // Search buffer: no need to make dynamic as this search is very transient.
     ImGuiID         FocusScope;
     ImGuiID         FocusScope;
     int             LastRequestFrame = 0;
     int             LastRequestFrame = 0;
     float           LastRequestTime = 0.0f;
     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;
     short                   TooltipOverrideCount;
     ImVector<char>          ClipboardHandlerData;               // If no custom clipboard handler is defined
     ImVector<char>          ClipboardHandlerData;               // If no custom clipboard handler is defined
     ImVector<ImGuiID>       MenusIdSubmittedThisFrame;          // A list of menu IDs that were rendered at least once
     ImVector<ImGuiID>       MenusIdSubmittedThisFrame;          // A list of menu IDs that were rendered at least once
-    ImGuiTypingSelectData   TypingSelectData;
+    ImGuiTypingSelectState  TypingSelectState;                  // State for GetTypingSelectRequest()
 
 
     // Platform support
     // Platform support
     ImGuiPlatformImeData    PlatformImeData;                    // Data updated by current frame
     ImGuiPlatformImeData    PlatformImeData;                    // Data updated by current frame
@@ -3124,7 +3126,7 @@ namespace ImGui
 
 
     // Typing-Select API
     // Typing-Select API
     IMGUI_API ImGuiTypingSelectRequest* GetTypingSelectRequest(ImGuiTypingSelectFlags flags = ImGuiTypingSelectFlags_None);
     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)
     // 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);
     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          DebugNodeTable(ImGuiTable* table);
     IMGUI_API void          DebugNodeTableSettings(ImGuiTableSettings* settings);
     IMGUI_API void          DebugNodeTableSettings(ImGuiTableSettings* settings);
     IMGUI_API void          DebugNodeInputTextState(ImGuiInputTextState* state);
     IMGUI_API void          DebugNodeInputTextState(ImGuiInputTextState* state);
+    IMGUI_API void          DebugNodeTypingSelectState(ImGuiTypingSelectState* state);
     IMGUI_API void          DebugNodeWindow(ImGuiWindow* window, const char* label);
     IMGUI_API void          DebugNodeWindow(ImGuiWindow* window, const char* label);
     IMGUI_API void          DebugNodeWindowSettings(ImGuiWindowSettings* settings);
     IMGUI_API void          DebugNodeWindowSettings(ImGuiWindowSettings* settings);
     IMGUI_API void          DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label);
     IMGUI_API void          DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label);

+ 53 - 21
imgui_widgets.cpp

@@ -6619,13 +6619,14 @@ bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags
 ImGuiTypingSelectRequest* ImGui::GetTypingSelectRequest(ImGuiTypingSelectFlags flags)
 ImGuiTypingSelectRequest* ImGui::GetTypingSelectRequest(ImGuiTypingSelectFlags flags)
 {
 {
     ImGuiContext& g = *GImGui;
     ImGuiContext& g = *GImGui;
-    ImGuiTypingSelectData* data = &g.TypingSelectData;
+    ImGuiTypingSelectState* data = &g.TypingSelectState;
     ImGuiTypingSelectRequest* out_request = &data->Request;
     ImGuiTypingSelectRequest* out_request = &data->Request;
 
 
     // Clear buffer
     // 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)
     if (data->SearchBuffer[0] != 0)
     {
     {
-        const float TYPING_SELECT_RESET_TIMER = 1.70f; // FIXME: Potentially move to IO config.
         bool clear_buffer = false;
         bool clear_buffer = false;
         clear_buffer |= (g.NavFocusScopeId != data->FocusScope);
         clear_buffer |= (g.NavFocusScopeId != data->FocusScope);
         clear_buffer |= (data->LastRequestTime + TYPING_SELECT_RESET_TIMER < g.Time);
         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;
         clear_buffer |= IsKeyPressed(ImGuiKey_Backspace) && (flags & ImGuiTypingSelectFlags_AllowBackspace) == 0;
         //if (clear_buffer) { IMGUI_DEBUG_LOG("GetTypingSelectRequest(): Clear SearchBuffer.\n"); }
         //if (clear_buffer) { IMGUI_DEBUG_LOG("GetTypingSelectRequest(): Clear SearchBuffer.\n"); }
         if (clear_buffer)
         if (clear_buffer)
-            data->SearchBuffer[0] = 0;
+            data->Clear();
     }
     }
 
 
     // Append to buffer
     // Append to buffer
     const int buffer_max_len = IM_ARRAYSIZE(data->SearchBuffer) - 1;
     const int buffer_max_len = IM_ARRAYSIZE(data->SearchBuffer) - 1;
     int buffer_len = (int)strlen(data->SearchBuffer);
     int buffer_len = (int)strlen(data->SearchBuffer);
-    bool buffer_changed = false;
+    bool select_request = false;
     for (ImWchar w : g.IO.InputQueueCharacters)
     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;
             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);
     g.IO.InputQueueCharacters.resize(0);
+
+    // Handle backspace
     if ((flags & ImGuiTypingSelectFlags_AllowBackspace) && IsKeyPressed(ImGuiKey_Backspace, 0, ImGuiInputFlags_Repeat))
     if ((flags & ImGuiTypingSelectFlags_AllowBackspace) && IsKeyPressed(ImGuiKey_Backspace, 0, ImGuiInputFlags_Repeat))
     {
     {
         char* p = (char*)(void*)ImTextFindPreviousUtf8Codepoint(data->SearchBuffer, data->SearchBuffer + buffer_len);
         char* p = (char*)(void*)ImTextFindPreviousUtf8Codepoint(data->SearchBuffer, data->SearchBuffer + buffer_len);
         *p = 0;
         *p = 0;
         buffer_len = (int)(p - data->SearchBuffer);
         buffer_len = (int)(p - data->SearchBuffer);
     }
     }
+
+    // Return request if any
     if (buffer_len == 0)
     if (buffer_len == 0)
         return NULL;
         return NULL;
-
-    if (buffer_changed)
+    if (select_request)
     {
     {
         data->FocusScope = g.NavFocusScopeId;
         data->FocusScope = g.NavFocusScopeId;
         data->LastRequestFrame = g.FrameCount;
         data->LastRequestFrame = g.FrameCount;
         data->LastRequestTime = (float)g.Time;
         data->LastRequestTime = (float)g.Time;
     }
     }
-
-    // Return request if any
     out_request->Flags = flags;
     out_request->Flags = flags;
     out_request->SearchBufferLen = buffer_len;
     out_request->SearchBufferLen = buffer_len;
     out_request->SearchBuffer = data->SearchBuffer;
     out_request->SearchBuffer = data->SearchBuffer;
@@ -6691,8 +6703,10 @@ ImGuiTypingSelectRequest* ImGui::GetTypingSelectRequest(ImGuiTypingSelectFlags f
         for (; p < buf_end; p += c0_len)
         for (; p < buf_end; p += c0_len)
             if (memcmp(buf_begin, p, (size_t)c0_len) != 0)
             if (memcmp(buf_begin, p, (size_t)c0_len) != 0)
                 break;
                 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;
     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.
 // 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.
 // 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.
 // - 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)
     if (req->SelectRequest == false)
         return -1;
         return -1;
+
+    ImGuiContext& g = *GImGui;
+    g.NavDisableMouseHover = true;
     if (req->SingleCharMode && (req->Flags & ImGuiTypingSelectFlags_AllowSingleCharMode))
     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.
         // 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;
         int first_match_idx = -1;
         bool return_next_match = false;
         bool return_next_match = false;
@@ -6755,6 +6776,17 @@ int ImGui::TypingSelectFindResult(ImGuiTypingSelectRequest* req, int items_count
     return longest_match_idx;
     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
 // [SECTION] Widgets: Multi-Select support