From 1674fe96ff57e4540b6c1860e61aa7fcc06642fe Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 5 Sep 2024 16:31:41 +0200 Subject: [PATCH] InputText: amends: add stb_textedit_text() api. (#7925) It seems sensible to push this change in stb_textedit repo eventually. --- imgui_internal.h | 1 + imgui_widgets.cpp | 23 +++++++++++------- imstb_textedit.h | 61 ++++++++++++++++++++++++++++------------------- 3 files changed, 52 insertions(+), 33 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 4dfcfce9a..e6f3f8ec5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1130,6 +1130,7 @@ struct IMGUI_API ImGuiInputTextState void ClearText() { CurLenA = 0; TextA[0] = 0; CursorClamp(); } void ClearFreeMemory() { TextA.clear(); InitialTextA.clear(); } void OnKeyPressed(int key); // Cannot be inline because we call in code in stb_textedit.h implementation + void OnCharPressed(unsigned int c); // Cursor & Selection void CursorAnimReset(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 64e701407..f35563119 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3889,7 +3889,6 @@ namespace ImStb static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->CurLenA; } static char STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { IM_ASSERT(idx <= obj->CurLenA); return obj->TextA[idx]; } static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { unsigned int c; ImTextCharFromUtf8(&c, obj->TextA.Data + line_start_idx + char_idx, obj->TextA.Data + obj->TextA.Size); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance((ImWchar)c) * g.FontScale; } -static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x200000 ? 0 : key; } static char STB_TEXTEDIT_NEWLINE = '\n'; static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx) { @@ -4101,12 +4100,18 @@ ImGuiInputTextState::~ImGuiInputTextState() void ImGuiInputTextState::OnKeyPressed(int key) { - //We prematurely convert the key to a UTF8 byte sequence, even for keys where that doesn't even make sense (e.g. arrow keys). - //Not optimal but stb_textedit_key will only use the UTF8 values for valid character keys anyways. - //The changes we had to make to stb_textedit_key make it very much UTF8 specific which is not too great. + stb_textedit_key(this, Stb, key); + CursorFollow = true; + CursorAnimReset(); +} + +void ImGuiInputTextState::OnCharPressed(unsigned int c) +{ + // Convert the key to a UTF8 byte sequence. + // The changes we had to make to stb_textedit_key made it very much UTF-8 specific which is not too great. char utf8[5]; - ImTextCharToUtf8(utf8, key); - stb_textedit_key(this, Stb, key, utf8, (int)strlen(utf8)); + ImTextCharToUtf8(utf8, c); + stb_textedit_text(this, Stb, utf8, (int)strlen(utf8)); CursorFollow = true; CursorAnimReset(); } @@ -4674,7 +4679,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { unsigned int c = '\t'; // Insert TAB if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data)) - state->OnKeyPressed((int)c); + state->OnCharPressed(c); } // FIXME: Implement Shift+Tab /* @@ -4697,7 +4702,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (c == '\t') // Skip Tab, see above. continue; if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data)) - state->OnKeyPressed((int)c); + state->OnCharPressed(c); } // Consume characters @@ -4781,7 +4786,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { unsigned int c = '\n'; // Insert new line if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data)) - state->OnKeyPressed((int)c); + state->OnCharPressed(c); } } else if (is_cancel) diff --git a/imstb_textedit.h b/imstb_textedit.h index 22122c08b..b7a761c85 100644 --- a/imstb_textedit.h +++ b/imstb_textedit.h @@ -211,6 +211,7 @@ // int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) // int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len) // void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key) +// void stb_textedit_text(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int text_len) // // Each of these functions potentially updates the string and updates the // state. @@ -245,7 +246,12 @@ // various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit // set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is // clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to -// anything other type you wante before including. +// anything other type you want before including. +// if the STB_TEXTEDIT_KEYTOTEXT function is defined, selected keys are +// transformed into text and stb_textedit_text() is automatically called. +// +// text: [DEAR IMGUI] added 2024-09 +// call this to text inputs sent to the textfield. // // // When rendering, you can read the cursor position and selection state from @@ -733,37 +739,44 @@ static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditS #define STB_TEXTEDIT_KEYTYPE int #endif +// [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility. +static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len) +{ + // can't add newline in single-line mode + if (text[0] == '\n' && state->single_line) + return; + + if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) { + stb_text_makeundo_replace(str, state, state->cursor, 1, 1); + STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1); + if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) { + state->cursor += text_len; + state->has_preferred_x = 0; + } + } + else { + stb_textedit_delete_selection(str, state); // implicitly clamps + if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) { + stb_text_makeundo_insert(state, state->cursor, text_len); + state->cursor += text_len; + state->has_preferred_x = 0; + } + } +} + // API key: process a keyboard input -//[DEAR IMGUI] In addition to the key we also pass in the decoded UTF8 byte sequence, if it is a character key. -//This is a bit ugly and only makes sense for UTF8. One could think of other solutions that wouldn't make this function so UTF8 specific. -//If the idea is to push the changes upstream to stb_textedit it might be worth thinking about but since this is just for [DEAR IMGUI], it might not be worth the complication -static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key, const IMSTB_TEXTEDIT_CHARTYPE* decoded, int decoded_size) +static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key) { retry: switch (key) { default: { +#ifdef STB_TEXTEDIT_KEYTOTEXT int c = STB_TEXTEDIT_KEYTOTEXT(key); if (c > 0) { - // can't add newline in single-line mode - if (c == '\n' && state->single_line) - break; - - if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) { - stb_text_makeundo_replace(str, state, state->cursor, 1, 1); - STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1); - if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, decoded, decoded_size)) { - state->cursor += decoded_size; - state->has_preferred_x = 0; - } - } else { - stb_textedit_delete_selection(str,state); // implicitly clamps - if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, decoded, decoded_size)) { - stb_text_makeundo_insert(state, state->cursor, decoded_size); - state->cursor += decoded_size; - state->has_preferred_x = 0; - } - } + IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c; + stb_textedit_text(str, state, &ch, 1); } +#endif break; }