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();
}
// 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"))

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 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<char> ClipboardHandlerData; // If no custom clipboard handler is defined
ImVector<ImGuiID> 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<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)
{
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