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

# Conflicts:
#	imgui.cpp
#	imgui_internal.h
This commit is contained in:
ocornut 2023-09-14 17:13:13 +02:00
parent 32171a8b0c
commit 661a70fc79
3 changed files with 69 additions and 27 deletions

View File

@ -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"))

View File

@ -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);

View File

@ -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); char w_buf[5];
if (buffer_len + utf8_len > buffer_max_len) ImTextCharToUtf8(w_buf, (unsigned int)w);
break; if (data->SingleCharModeLock && w_len == out_request->SingleCharSize && memcmp(w_buf, data->SearchBuffer, w_len) == 0)
ImTextCharToUtf8(data->SearchBuffer + buffer_len, (unsigned int)w); {
buffer_len += utf8_len; select_request = true; // Same character: don't need to append to buffer.
buffer_changed = true; 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 (select_request)
if (buffer_changed)
{ {
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); const int single_char_count = (p == buf_end) ? (out_request->SearchBufferLen / c0_len) : 0;
out_request->SingleCharSize = out_request->SingleCharMode ? (ImS8)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: // When SingleCharMode is set:
// - it may make less sense to be displaying a tooltip. // - 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. // if your SetNextItemSelectionData() values are index, you can obtain it from ImGuiMultiSelectIO::NavIdItem, otherwise from g.NavLastValidSelectionUserData.
int ImGui::TypingSelectFindResult(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx) 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