|
@@ -128,7 +128,7 @@ static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1);
|
|
|
// For InputTextEx()
|
|
|
static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard = false);
|
|
|
static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end);
|
|
|
-static ImVec2 InputTextCalcTextSizeW(ImGuiContext* ctx, const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false);
|
|
|
+static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end, const char** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false);
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
// [SECTION] Widgets: Text, etc.
|
|
@@ -3842,7 +3842,8 @@ static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char**
|
|
|
return line_count;
|
|
|
}
|
|
|
|
|
|
-static ImVec2 InputTextCalcTextSizeW(ImGuiContext* ctx, const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line)
|
|
|
+// FIXME: Ideally we'd share code with ImFont::CalcTextSizeA()
|
|
|
+static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end, const char** remaining, ImVec2* out_offset, bool stop_on_new_line)
|
|
|
{
|
|
|
ImGuiContext& g = *ctx;
|
|
|
ImFont* font = g.Font;
|
|
@@ -3852,10 +3853,15 @@ static ImVec2 InputTextCalcTextSizeW(ImGuiContext* ctx, const ImWchar* text_begi
|
|
|
ImVec2 text_size = ImVec2(0, 0);
|
|
|
float line_width = 0.0f;
|
|
|
|
|
|
- const ImWchar* s = text_begin;
|
|
|
+ const char* s = text_begin;
|
|
|
while (s < text_end)
|
|
|
{
|
|
|
- unsigned int c = (unsigned int)(*s++);
|
|
|
+ unsigned int c = (unsigned int)*s;
|
|
|
+ if (c < 0x80)
|
|
|
+ s += 1;
|
|
|
+ else
|
|
|
+ s += ImTextCharFromUtf8(&c, s, text_end);
|
|
|
+
|
|
|
if (c == '\n')
|
|
|
{
|
|
|
text_size.x = ImMax(text_size.x, line_width);
|
|
@@ -3868,7 +3874,7 @@ static ImVec2 InputTextCalcTextSizeW(ImGuiContext* ctx, const ImWchar* text_begi
|
|
|
if (c == '\r')
|
|
|
continue;
|
|
|
|
|
|
- const float char_width = font->GetCharAdvance((ImWchar)c) * scale;
|
|
|
+ const float char_width = ((int)c < font->IndexAdvanceX.Size ? font->IndexAdvanceX.Data[c] : font->FallbackAdvanceX) * scale;
|
|
|
line_width += char_width;
|
|
|
}
|
|
|
|
|
@@ -3888,19 +3894,21 @@ static ImVec2 InputTextCalcTextSizeW(ImGuiContext* ctx, const ImWchar* text_begi
|
|
|
}
|
|
|
|
|
|
// Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar)
|
|
|
+// With our UTF-8 use of stb_textedit:
|
|
|
+// - STB_TEXTEDIT_GETCHAR is nothing more than a a "GETBYTE". It's only used to compare to ascii or to copy blocks of text so we are fine.
|
|
|
+// - One exception is the STB_TEXTEDIT_IS_SPACE feature which would expect a full char in order to handle full-width space such as 0x3000 (see ImCharIsBlankW).
|
|
|
+// - ...but we don't use that feature.
|
|
|
namespace ImStb
|
|
|
{
|
|
|
-
|
|
|
-static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->CurLenW; }
|
|
|
-static ImWchar STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { IM_ASSERT(idx <= obj->CurLenW); return obj->TextW[idx]; }
|
|
|
-static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx + char_idx]; if (c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance(c) * g.FontScale; }
|
|
|
-static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x200000 ? 0 : key; }
|
|
|
-static ImWchar STB_TEXTEDIT_NEWLINE = '\n';
|
|
|
+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 char STB_TEXTEDIT_NEWLINE = '\n';
|
|
|
static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx)
|
|
|
{
|
|
|
- const ImWchar* text = obj->TextW.Data;
|
|
|
- const ImWchar* text_remaining = NULL;
|
|
|
- const ImVec2 size = InputTextCalcTextSizeW(obj->Ctx, text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true);
|
|
|
+ const char* text = obj->TextA.Data;
|
|
|
+ const char* text_remaining = NULL;
|
|
|
+ const ImVec2 size = InputTextCalcTextSize(obj->Ctx, text + line_start_idx, text + obj->CurLenA, &text_remaining, NULL, true);
|
|
|
r->x0 = 0.0f;
|
|
|
r->x1 = size.x;
|
|
|
r->baseline_y_delta = size.y;
|
|
@@ -3909,6 +3917,25 @@ static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* ob
|
|
|
r->num_chars = (int)(text_remaining - (text + line_start_idx));
|
|
|
}
|
|
|
|
|
|
+#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL
|
|
|
+#define IMSTB_TEXTEDIT_GETPREVCHARINDEX IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL
|
|
|
+
|
|
|
+static int IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx)
|
|
|
+{
|
|
|
+ if (idx >= obj->CurLenA)
|
|
|
+ return obj->CurLenA + 1;
|
|
|
+ unsigned int c;
|
|
|
+ return idx + ImTextCharFromUtf8(&c, obj->TextA.Data + idx, obj->TextA.Data + obj->TextA.Size);
|
|
|
+}
|
|
|
+
|
|
|
+static int IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx)
|
|
|
+{
|
|
|
+ if (idx <= 0)
|
|
|
+ return -1;
|
|
|
+ const char* p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, obj->TextA.Data + idx);
|
|
|
+ return (int)(p - obj->TextA.Data);
|
|
|
+}
|
|
|
+
|
|
|
static bool ImCharIsSeparatorW(unsigned int c)
|
|
|
{
|
|
|
static const unsigned int separator_list[] =
|
|
@@ -3929,10 +3956,15 @@ static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx)
|
|
|
if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0)
|
|
|
return 0;
|
|
|
|
|
|
- bool prev_white = ImCharIsBlankW(obj->TextW[idx - 1]);
|
|
|
- bool prev_separ = ImCharIsSeparatorW(obj->TextW[idx - 1]);
|
|
|
- bool curr_white = ImCharIsBlankW(obj->TextW[idx]);
|
|
|
- bool curr_separ = ImCharIsSeparatorW(obj->TextW[idx]);
|
|
|
+ const char* curr_p = obj->TextA.Data + idx;
|
|
|
+ const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, curr_p);
|
|
|
+ unsigned int curr_c; ImTextCharFromUtf8(&curr_c, curr_p, obj->TextA.Data + obj->TextA.Size);
|
|
|
+ unsigned int prev_c; ImTextCharFromUtf8(&prev_c, prev_p, obj->TextA.Data + obj->TextA.Size);
|
|
|
+
|
|
|
+ bool prev_white = ImCharIsBlankW(prev_c);
|
|
|
+ bool prev_separ = ImCharIsSeparatorW(prev_c);
|
|
|
+ bool curr_white = ImCharIsBlankW(curr_c);
|
|
|
+ bool curr_separ = ImCharIsSeparatorW(curr_c);
|
|
|
return ((prev_white || prev_separ) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ);
|
|
|
}
|
|
|
static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx)
|
|
@@ -3940,63 +3972,83 @@ static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx)
|
|
|
if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0)
|
|
|
return 0;
|
|
|
|
|
|
- bool prev_white = ImCharIsBlankW(obj->TextW[idx]);
|
|
|
- bool prev_separ = ImCharIsSeparatorW(obj->TextW[idx]);
|
|
|
- bool curr_white = ImCharIsBlankW(obj->TextW[idx - 1]);
|
|
|
- bool curr_separ = ImCharIsSeparatorW(obj->TextW[idx - 1]);
|
|
|
+ const char* curr_p = obj->TextA.Data + idx;
|
|
|
+ const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, curr_p);
|
|
|
+ unsigned int prev_c; ImTextCharFromUtf8(&prev_c, curr_p, obj->TextA.Data + obj->TextA.Size);
|
|
|
+ unsigned int curr_c; ImTextCharFromUtf8(&curr_c, prev_p, obj->TextA.Data + obj->TextA.Size);
|
|
|
+
|
|
|
+ bool prev_white = ImCharIsBlankW(prev_c);
|
|
|
+ bool prev_separ = ImCharIsSeparatorW(prev_c);
|
|
|
+ bool curr_white = ImCharIsBlankW(curr_c);
|
|
|
+ bool curr_separ = ImCharIsSeparatorW(curr_c);
|
|
|
return ((prev_white) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ);
|
|
|
}
|
|
|
-static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; }
|
|
|
-static int STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; }
|
|
|
-static int STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; }
|
|
|
+static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx)
|
|
|
+{
|
|
|
+ idx = IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx);
|
|
|
+ while (idx >= 0 && !is_word_boundary_from_right(obj, idx))
|
|
|
+ idx = IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx);
|
|
|
+ return idx < 0 ? 0 : idx;
|
|
|
+}
|
|
|
+static int STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx)
|
|
|
+{
|
|
|
+ int len = obj->CurLenA;
|
|
|
+ idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx);
|
|
|
+ while (idx < len && !is_word_boundary_from_left(obj, idx))
|
|
|
+ idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx);
|
|
|
+ return idx > len ? len : idx;
|
|
|
+}
|
|
|
+static int STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx)
|
|
|
+{
|
|
|
+ idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx);
|
|
|
+ int len = obj->CurLenA;
|
|
|
+ while (idx < len && !is_word_boundary_from_right(obj, idx))
|
|
|
+ idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx);
|
|
|
+ return idx > len ? len : idx;
|
|
|
+}
|
|
|
static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx) { ImGuiContext& g = *obj->Ctx; if (g.IO.ConfigMacOSXBehaviors) return STB_TEXTEDIT_MOVEWORDRIGHT_MAC(obj, idx); else return STB_TEXTEDIT_MOVEWORDRIGHT_WIN(obj, idx); }
|
|
|
-#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h
|
|
|
-#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL
|
|
|
+#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h
|
|
|
+#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL
|
|
|
|
|
|
static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n)
|
|
|
{
|
|
|
- ImWchar* dst = obj->TextW.Data + pos;
|
|
|
+ char* dst = obj->TextA.Data + pos;
|
|
|
|
|
|
- // We maintain our buffer length in both UTF-8 and wchar formats
|
|
|
obj->Edited = true;
|
|
|
- obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n);
|
|
|
- obj->CurLenW -= n;
|
|
|
+ obj->CurLenA -= n;
|
|
|
|
|
|
// Offset remaining text (FIXME-OPT: Use memmove)
|
|
|
- const ImWchar* src = obj->TextW.Data + pos + n;
|
|
|
- while (ImWchar c = *src++)
|
|
|
+ const char* src = obj->TextA.Data + pos + n;
|
|
|
+ while (char c = *src++)
|
|
|
*dst++ = c;
|
|
|
*dst = '\0';
|
|
|
}
|
|
|
|
|
|
-static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const ImWchar* new_text, int new_text_len)
|
|
|
+static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const char* new_text, int new_text_len)
|
|
|
{
|
|
|
const bool is_resizable = (obj->Flags & ImGuiInputTextFlags_CallbackResize) != 0;
|
|
|
- const int text_len = obj->CurLenW;
|
|
|
+ const int text_len = obj->CurLenA;
|
|
|
IM_ASSERT(pos <= text_len);
|
|
|
|
|
|
- const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len);
|
|
|
- if (!is_resizable && (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufCapacityA))
|
|
|
+ if (!is_resizable && (new_text_len + obj->CurLenA + 1 > obj->BufCapacityA))
|
|
|
return false;
|
|
|
|
|
|
// Grow internal buffer if needed
|
|
|
- if (new_text_len + text_len + 1 > obj->TextW.Size)
|
|
|
+ if (new_text_len + text_len + 1 > obj->TextA.Size)
|
|
|
{
|
|
|
if (!is_resizable)
|
|
|
return false;
|
|
|
- IM_ASSERT(text_len < obj->TextW.Size);
|
|
|
- obj->TextW.resize(text_len + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1);
|
|
|
+ obj->TextA.resize(text_len + ImClamp(new_text_len, 32, ImMax(256, new_text_len)) + 1);
|
|
|
}
|
|
|
|
|
|
- ImWchar* text = obj->TextW.Data;
|
|
|
+ char* text = obj->TextA.Data;
|
|
|
if (pos != text_len)
|
|
|
- memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar));
|
|
|
- memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar));
|
|
|
+ memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos));
|
|
|
+ memcpy(text + pos, new_text, (size_t)new_text_len);
|
|
|
|
|
|
obj->Edited = true;
|
|
|
- obj->CurLenW += new_text_len;
|
|
|
- obj->CurLenA += new_text_len_utf8;
|
|
|
- obj->TextW[obj->CurLenW] = '\0';
|
|
|
+ obj->CurLenA += new_text_len;
|
|
|
+ obj->TextA[obj->CurLenA] = '\0';
|
|
|
|
|
|
return true;
|
|
|
}
|
|
@@ -4028,8 +4080,8 @@ static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const Im
|
|
|
// the stb_textedit_paste() function creates two separate records, so we perform it manually. (FIXME: Report to nothings/stb?)
|
|
|
static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)
|
|
|
{
|
|
|
- stb_text_makeundo_replace(str, state, 0, str->CurLenW, text_len);
|
|
|
- ImStb::STB_TEXTEDIT_DELETECHARS(str, 0, str->CurLenW);
|
|
|
+ stb_text_makeundo_replace(str, state, 0, str->CurLenA, text_len);
|
|
|
+ ImStb::STB_TEXTEDIT_DELETECHARS(str, 0, str->CurLenA);
|
|
|
state->cursor = state->select_start = state->select_end = 0;
|
|
|
if (text_len <= 0)
|
|
|
return;
|
|
@@ -4063,15 +4115,26 @@ void ImGuiInputTextState::OnKeyPressed(int key)
|
|
|
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, c);
|
|
|
+ stb_textedit_text(this, Stb, utf8, (int)strlen(utf8));
|
|
|
+ CursorFollow = true;
|
|
|
+ CursorAnimReset();
|
|
|
+}
|
|
|
+
|
|
|
// Those functions are not inlined in imgui_internal.h, allowing us to hide ImStbTexteditState from that header.
|
|
|
void ImGuiInputTextState::CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking
|
|
|
-void ImGuiInputTextState::CursorClamp() { Stb->cursor = ImMin(Stb->cursor, CurLenW); Stb->select_start = ImMin(Stb->select_start, CurLenW); Stb->select_end = ImMin(Stb->select_end, CurLenW); }
|
|
|
+void ImGuiInputTextState::CursorClamp() { Stb->cursor = ImMin(Stb->cursor, CurLenA); Stb->select_start = ImMin(Stb->select_start, CurLenA); Stb->select_end = ImMin(Stb->select_end, CurLenA); }
|
|
|
bool ImGuiInputTextState::HasSelection() const { return Stb->select_start != Stb->select_end; }
|
|
|
void ImGuiInputTextState::ClearSelection() { Stb->select_start = Stb->select_end = Stb->cursor; }
|
|
|
int ImGuiInputTextState::GetCursorPos() const { return Stb->cursor; }
|
|
|
int ImGuiInputTextState::GetSelectionStart() const { return Stb->select_start; }
|
|
|
int ImGuiInputTextState::GetSelectionEnd() const { return Stb->select_end; }
|
|
|
-void ImGuiInputTextState::SelectAll() { Stb->select_start = 0; Stb->cursor = Stb->select_end = CurLenW; Stb->has_preferred_x = 0; }
|
|
|
+void ImGuiInputTextState::SelectAll() { Stb->select_start = 0; Stb->cursor = Stb->select_end = CurLenA; Stb->has_preferred_x = 0; }
|
|
|
void ImGuiInputTextState::ReloadUserBufAndSelectAll() { ReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; }
|
|
|
void ImGuiInputTextState::ReloadUserBufAndKeepSelection() { ReloadUserBuf = true; ReloadSelectionStart = Stb->select_start; ReloadSelectionEnd = Stb->select_end; }
|
|
|
void ImGuiInputTextState::ReloadUserBufAndMoveToEnd() { ReloadUserBuf = true; ReloadSelectionStart = ReloadSelectionEnd = INT_MAX; }
|
|
@@ -4244,26 +4307,21 @@ static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, Im
|
|
|
// FIXME: Ideally we should transition toward (1) making InsertChars()/DeleteChars() update undo-stack (2) discourage (and keep reconcile) or obsolete (and remove reconcile) accessing buffer directly.
|
|
|
static void InputTextReconcileUndoStateAfterUserCallback(ImGuiInputTextState* state, const char* new_buf_a, int new_length_a)
|
|
|
{
|
|
|
- ImGuiContext& g = *GImGui;
|
|
|
- const ImWchar* old_buf = state->TextW.Data;
|
|
|
- const int old_length = state->CurLenW;
|
|
|
- const int new_length = ImTextCountCharsFromUtf8(new_buf_a, new_buf_a + new_length_a);
|
|
|
- g.TempBuffer.reserve_discard((new_length + 1) * sizeof(ImWchar));
|
|
|
- ImWchar* new_buf = (ImWchar*)(void*)g.TempBuffer.Data;
|
|
|
- ImTextStrFromUtf8(new_buf, new_length + 1, new_buf_a, new_buf_a + new_length_a);
|
|
|
-
|
|
|
- const int shorter_length = ImMin(old_length, new_length);
|
|
|
+ const char* old_buf = state->CallbackTextBackup.Data;
|
|
|
+ const int old_length = state->CallbackTextBackup.Size - 1;
|
|
|
+
|
|
|
+ const int shorter_length = ImMin(old_length, new_length_a);
|
|
|
int first_diff;
|
|
|
for (first_diff = 0; first_diff < shorter_length; first_diff++)
|
|
|
- if (old_buf[first_diff] != new_buf[first_diff])
|
|
|
+ if (old_buf[first_diff] != new_buf_a[first_diff])
|
|
|
break;
|
|
|
- if (first_diff == old_length && first_diff == new_length)
|
|
|
+ if (first_diff == old_length && first_diff == new_length_a)
|
|
|
return;
|
|
|
|
|
|
- int old_last_diff = old_length - 1;
|
|
|
- int new_last_diff = new_length - 1;
|
|
|
+ int old_last_diff = old_length - 1;
|
|
|
+ int new_last_diff = new_length_a - 1;
|
|
|
for (; old_last_diff >= first_diff && new_last_diff >= first_diff; old_last_diff--, new_last_diff--)
|
|
|
- if (old_buf[old_last_diff] != new_buf[new_last_diff])
|
|
|
+ if (old_buf[old_last_diff] != new_buf_a[new_last_diff])
|
|
|
break;
|
|
|
|
|
|
const int insert_len = new_last_diff - first_diff + 1;
|
|
@@ -4271,7 +4329,7 @@ static void InputTextReconcileUndoStateAfterUserCallback(ImGuiInputTextState* st
|
|
|
if (insert_len > 0 || delete_len > 0)
|
|
|
if (IMSTB_TEXTEDIT_CHARTYPE* p = stb_text_createundo(&state->Stb->undostate, first_diff, delete_len, insert_len))
|
|
|
for (int i = 0; i < delete_len; i++)
|
|
|
- p[i] = ImStb::STB_TEXTEDIT_GETCHAR(state, first_diff + i);
|
|
|
+ p[i] = old_buf[first_diff + i];
|
|
|
}
|
|
|
|
|
|
// As InputText() retain textual data and we currently provide a path for user to not retain it (via local variables)
|
|
@@ -4436,17 +4494,14 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
// Preserve cursor position and undo/redo stack if we come back to same widget
|
|
|
// FIXME: Since we reworked this on 2022/06, may want to differentiate recycle_cursor vs recycle_undostate?
|
|
|
bool recycle_state = (state->ID == id && !init_changed_specs && !init_reload_from_user_buf);
|
|
|
- if (recycle_state && (state->CurLenA != buf_len || (state->TextAIsValid && strncmp(state->TextA.Data, buf, buf_len) != 0)))
|
|
|
+ if (recycle_state && (state->CurLenA != buf_len || (strncmp(state->TextA.Data, buf, buf_len) != 0)))
|
|
|
recycle_state = false;
|
|
|
|
|
|
// Start edition
|
|
|
- const char* buf_end = NULL;
|
|
|
state->ID = id;
|
|
|
- state->TextW.resize(buf_size + 1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data is always pointing to at least an empty string.
|
|
|
- state->TextA.resize(0);
|
|
|
- state->TextAIsValid = false; // TextA is not valid yet (we will display buf until then)
|
|
|
- state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, buf_size, buf, NULL, &buf_end);
|
|
|
- state->CurLenA = (int)(buf_end - buf); // We can't get the result from ImStrncpy() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8.
|
|
|
+ state->TextA.resize(buf_size + 1); // we use +1 to make sure that .Data is always pointing to at least an empty string.
|
|
|
+ state->CurLenA = (int)strlen(buf);
|
|
|
+ memcpy(state->TextA.Data, buf, state->CurLenA + 1);
|
|
|
|
|
|
if (recycle_state)
|
|
|
{
|
|
@@ -4528,20 +4583,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
bool value_changed = false;
|
|
|
bool validated = false;
|
|
|
|
|
|
- // When read-only we always use the live data passed to the function
|
|
|
- // FIXME-OPT: Because our selection/cursor code currently needs the wide text we need to convert it when active, which is not ideal :(
|
|
|
- if (is_readonly && state != NULL && (render_cursor || render_selection))
|
|
|
- {
|
|
|
- const char* buf_end = NULL;
|
|
|
- state->TextW.resize(buf_size + 1);
|
|
|
- state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, state->TextW.Size, buf, NULL, &buf_end);
|
|
|
- state->CurLenA = (int)(buf_end - buf);
|
|
|
- state->CursorClamp();
|
|
|
- render_selection &= state->HasSelection();
|
|
|
- }
|
|
|
-
|
|
|
// Select the buffer to render.
|
|
|
- const bool buf_display_from_state = (render_cursor || render_selection || g.ActiveId == id) && !is_readonly && state && state->TextAIsValid;
|
|
|
+ const bool buf_display_from_state = (render_cursor || render_selection || g.ActiveId == id) && !is_readonly && state;
|
|
|
const bool is_displaying_hint = (hint != NULL && (buf_display_from_state ? state->TextA.Data : buf)[0] == 0);
|
|
|
|
|
|
// Password pushes a temporary font with only a fallback glyph
|
|
@@ -4561,11 +4604,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
}
|
|
|
|
|
|
// Process mouse inputs and character inputs
|
|
|
- int backup_current_text_length = 0;
|
|
|
if (g.ActiveId == id)
|
|
|
{
|
|
|
IM_ASSERT(state != NULL);
|
|
|
- backup_current_text_length = state->CurLenA;
|
|
|
state->Edited = false;
|
|
|
state->BufCapacityA = buf_size;
|
|
|
state->Flags = flags;
|
|
@@ -4646,7 +4687,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
|
|
|
/*
|
|
@@ -4669,7 +4710,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
|
|
@@ -4753,7 +4794,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)
|
|
@@ -4792,12 +4833,12 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
if (g.PlatformIO.Platform_SetClipboardTextFn != NULL)
|
|
|
{
|
|
|
const int ib = state->HasSelection() ? ImMin(state->Stb->select_start, state->Stb->select_end) : 0;
|
|
|
- const int ie = state->HasSelection() ? ImMax(state->Stb->select_start, state->Stb->select_end) : state->CurLenW;
|
|
|
- const int clipboard_data_len = ImTextCountUtf8BytesFromStr(state->TextW.Data + ib, state->TextW.Data + ie) + 1;
|
|
|
- char* clipboard_data = (char*)IM_ALLOC(clipboard_data_len * sizeof(char));
|
|
|
- ImTextStrToUtf8(clipboard_data, clipboard_data_len, state->TextW.Data + ib, state->TextW.Data + ie);
|
|
|
- SetClipboardText(clipboard_data);
|
|
|
- MemFree(clipboard_data);
|
|
|
+ const int ie = state->HasSelection() ? ImMax(state->Stb->select_start, state->Stb->select_end) : state->CurLenA;
|
|
|
+
|
|
|
+ char backup = state->TextA.Data[ie];
|
|
|
+ state->TextA.Data[ie] = 0; // A bit of a hack since SetClipboardText only takes null terminated strings
|
|
|
+ SetClipboardText(state->TextA.Data + ib);
|
|
|
+ state->TextA.Data[ie] = backup;
|
|
|
}
|
|
|
if (is_cut)
|
|
|
{
|
|
@@ -4813,15 +4854,17 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
{
|
|
|
// Filter pasted buffer
|
|
|
const int clipboard_len = (int)strlen(clipboard);
|
|
|
- ImWchar* clipboard_filtered = (ImWchar*)IM_ALLOC((clipboard_len + 1) * sizeof(ImWchar));
|
|
|
+ char* clipboard_filtered = (char*)IM_ALLOC(clipboard_len + 1);
|
|
|
int clipboard_filtered_len = 0;
|
|
|
for (const char* s = clipboard; *s != 0; )
|
|
|
{
|
|
|
unsigned int c;
|
|
|
- s += ImTextCharFromUtf8(&c, s, NULL);
|
|
|
+ int len = ImTextCharFromUtf8(&c, s, NULL);
|
|
|
+ s += len;
|
|
|
if (!InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data, true))
|
|
|
continue;
|
|
|
- clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c;
|
|
|
+ memcpy(clipboard_filtered + clipboard_filtered_len, s - len, len);
|
|
|
+ clipboard_filtered_len += len;
|
|
|
}
|
|
|
clipboard_filtered[clipboard_filtered_len] = 0;
|
|
|
if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation
|
|
@@ -4857,29 +4900,16 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
}
|
|
|
else if (strcmp(buf, state->InitialTextA.Data) != 0)
|
|
|
{
|
|
|
- // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents.
|
|
|
- // Push records into the undo stack so we can CTRL+Z the revert operation itself
|
|
|
apply_new_text = state->InitialTextA.Data;
|
|
|
apply_new_text_length = state->InitialTextA.Size - 1;
|
|
|
+
|
|
|
+ // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents.
|
|
|
+ // Push records into the undo stack so we can CTRL+Z the revert operation itself
|
|
|
value_changed = true;
|
|
|
- ImVector<ImWchar> w_text;
|
|
|
- if (apply_new_text_length > 0)
|
|
|
- {
|
|
|
- w_text.resize(ImTextCountCharsFromUtf8(apply_new_text, apply_new_text + apply_new_text_length) + 1);
|
|
|
- ImTextStrFromUtf8(w_text.Data, w_text.Size, apply_new_text, apply_new_text + apply_new_text_length);
|
|
|
- }
|
|
|
- stb_textedit_replace(state, state->Stb, w_text.Data, (apply_new_text_length > 0) ? (w_text.Size - 1) : 0);
|
|
|
+ stb_textedit_replace(state, state->Stb, state->InitialTextA.Data, state->InitialTextA.Size - 1);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // Apply ASCII value
|
|
|
- if (!is_readonly)
|
|
|
- {
|
|
|
- state->TextAIsValid = true;
|
|
|
- state->TextA.resize(state->TextW.Size * 4 + 1);
|
|
|
- ImTextStrToUtf8(state->TextA.Data, state->TextA.Size, state->TextW.Data, NULL);
|
|
|
- }
|
|
|
-
|
|
|
// When using 'ImGuiInputTextFlags_EnterReturnsTrue' as a special case we reapply the live buffer back to the input buffer
|
|
|
// before clearing ActiveId, even though strictly speaking it wasn't modified on this frame.
|
|
|
// If we didn't do that, code like InputInt() with ImGuiInputTextFlags_EnterReturnsTrue would fail.
|
|
@@ -4934,6 +4964,10 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
callback_data.Flags = flags;
|
|
|
callback_data.UserData = callback_user_data;
|
|
|
|
|
|
+ // FIXME-OPT: Undo stack reconcile needs a backup of the data until we rework API, see #7925
|
|
|
+ state->CallbackTextBackup.resize(state->CurLenA + 1);
|
|
|
+ memcpy(state->CallbackTextBackup.Data, state->TextA.Data, state->CurLenA + 1);
|
|
|
+
|
|
|
char* callback_buf = is_readonly ? buf : state->TextA.Data;
|
|
|
callback_data.EventKey = event_key;
|
|
|
callback_data.Buf = callback_buf;
|
|
@@ -4941,11 +4975,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
callback_data.BufSize = state->BufCapacityA;
|
|
|
callback_data.BufDirty = false;
|
|
|
|
|
|
- // We have to convert from wchar-positions to UTF-8-positions, which can be pretty slow (an incentive to ditch the ImWchar buffer, see https://github.com/nothings/stb/issues/188)
|
|
|
- ImWchar* text = state->TextW.Data;
|
|
|
- const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + state->Stb->cursor);
|
|
|
- const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + state->Stb->select_start);
|
|
|
- const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + state->Stb->select_end);
|
|
|
+ const int utf8_cursor_pos = callback_data.CursorPos = state->Stb->cursor;
|
|
|
+ const int utf8_selection_start = callback_data.SelectionStart = state->Stb->select_start;
|
|
|
+ const int utf8_selection_end = state->Stb->select_end;
|
|
|
|
|
|
// Call user code
|
|
|
callback(&callback_data);
|
|
@@ -4956,18 +4988,16 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
IM_ASSERT(callback_data.BufSize == state->BufCapacityA);
|
|
|
IM_ASSERT(callback_data.Flags == flags);
|
|
|
const bool buf_dirty = callback_data.BufDirty;
|
|
|
- if (callback_data.CursorPos != utf8_cursor_pos || buf_dirty) { state->Stb->cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); state->CursorFollow = true; }
|
|
|
- if (callback_data.SelectionStart != utf8_selection_start || buf_dirty) { state->Stb->select_start = (callback_data.SelectionStart == callback_data.CursorPos) ? state->Stb->cursor : ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); }
|
|
|
- if (callback_data.SelectionEnd != utf8_selection_end || buf_dirty) { state->Stb->select_end = (callback_data.SelectionEnd == callback_data.SelectionStart) ? state->Stb->select_start : ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); }
|
|
|
+ if (callback_data.CursorPos != utf8_cursor_pos || buf_dirty) { state->Stb->cursor = callback_data.CursorPos; state->CursorFollow = true; }
|
|
|
+ if (callback_data.SelectionStart != utf8_selection_start || buf_dirty) { state->Stb->select_start = (callback_data.SelectionStart == callback_data.CursorPos) ? state->Stb->cursor : callback_data.SelectionStart; }
|
|
|
+ if (callback_data.SelectionEnd != utf8_selection_end || buf_dirty) { state->Stb->select_end = (callback_data.SelectionEnd == callback_data.SelectionStart) ? state->Stb->select_start : callback_data.SelectionEnd; }
|
|
|
if (buf_dirty)
|
|
|
{
|
|
|
// Callback may update buffer and thus set buf_dirty even in read-only mode.
|
|
|
IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!
|
|
|
InputTextReconcileUndoStateAfterUserCallback(state, callback_data.Buf, callback_data.BufTextLen); // FIXME: Move the rest of this block inside function and rename to InputTextReconcileStateAfterUserCallback() ?
|
|
|
- if (callback_data.BufTextLen > backup_current_text_length && is_resizable)
|
|
|
- state->TextW.resize(state->TextW.Size + (callback_data.BufTextLen - backup_current_text_length)); // Worse case scenario resize
|
|
|
- state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, state->TextW.Size, callback_data.Buf, NULL);
|
|
|
state->CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen()
|
|
|
+ state->TextA.Size = state->CurLenA + 1;
|
|
|
state->CursorAnimReset();
|
|
|
}
|
|
|
}
|
|
@@ -4999,9 +5029,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
// Copy result to user buffer. This can currently only happen when (g.ActiveId == id)
|
|
|
if (apply_new_text != NULL)
|
|
|
{
|
|
|
- // We cannot test for 'backup_current_text_length != apply_new_text_length' here because we have no guarantee that the size
|
|
|
- // of our owned buffer matches the size of the string object held by the user, and by design we allow InputText() to be used
|
|
|
- // without any storage on user's side.
|
|
|
+ //// We cannot test for 'backup_current_text_length != apply_new_text_length' here because we have no guarantee that the size
|
|
|
+ //// of our owned buffer matches the size of the string object held by the user, and by design we allow InputText() to be used
|
|
|
+ //// without any storage on user's side.
|
|
|
IM_ASSERT(apply_new_text_length >= 0);
|
|
|
if (is_resizable)
|
|
|
{
|
|
@@ -5070,12 +5100,12 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
// - Measure text height (for scrollbar)
|
|
|
// We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort)
|
|
|
// FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8.
|
|
|
- const ImWchar* text_begin = state->TextW.Data;
|
|
|
+ const char* text_begin = state->TextA.Data;
|
|
|
ImVec2 cursor_offset, select_start_offset;
|
|
|
|
|
|
{
|
|
|
// Find lines numbers straddling 'cursor' (slot 0) and 'select_start' (slot 1) positions.
|
|
|
- const ImWchar* searches_input_ptr[2] = { NULL, NULL };
|
|
|
+ const char* searches_input_ptr[2] = { NULL, NULL };
|
|
|
int searches_result_line_no[2] = { -1000, -1000 };
|
|
|
int searches_remaining = 0;
|
|
|
if (render_cursor)
|
|
@@ -5095,14 +5125,12 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
// In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter.
|
|
|
searches_remaining += is_multiline ? 1 : 0;
|
|
|
int line_count = 0;
|
|
|
- //for (const ImWchar* s = text_begin; (s = (const ImWchar*)wcschr((const wchar_t*)s, (wchar_t)'\n')) != NULL; s++) // FIXME-OPT: Could use this when wchar_t are 16-bit
|
|
|
- for (const ImWchar* s = text_begin; *s != 0; s++)
|
|
|
- if (*s == '\n')
|
|
|
- {
|
|
|
- line_count++;
|
|
|
- if (searches_result_line_no[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_no[0] = line_count; if (--searches_remaining <= 0) break; }
|
|
|
- if (searches_result_line_no[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_no[1] = line_count; if (--searches_remaining <= 0) break; }
|
|
|
- }
|
|
|
+ for (const char* s = text_begin; (s = strchr(s, '\n')) != NULL; s++) // FIXME-OPT: memchr() would be faster?
|
|
|
+ {
|
|
|
+ line_count++;
|
|
|
+ if (searches_result_line_no[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_no[0] = line_count; if (--searches_remaining <= 0) break; }
|
|
|
+ if (searches_result_line_no[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_no[1] = line_count; if (--searches_remaining <= 0) break; }
|
|
|
+ }
|
|
|
line_count++;
|
|
|
if (searches_result_line_no[0] == -1)
|
|
|
searches_result_line_no[0] = line_count;
|
|
@@ -5110,11 +5138,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
searches_result_line_no[1] = line_count;
|
|
|
|
|
|
// Calculate 2d position by finding the beginning of the line and measuring distance
|
|
|
- cursor_offset.x = InputTextCalcTextSizeW(&g, ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x;
|
|
|
+ cursor_offset.x = InputTextCalcTextSize(&g, ImStrbol(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x;
|
|
|
cursor_offset.y = searches_result_line_no[0] * g.FontSize;
|
|
|
if (searches_result_line_no[1] >= 0)
|
|
|
{
|
|
|
- select_start_offset.x = InputTextCalcTextSizeW(&g, ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x;
|
|
|
+ select_start_offset.x = InputTextCalcTextSize(&g, ImStrbol(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x;
|
|
|
select_start_offset.y = searches_result_line_no[1] * g.FontSize;
|
|
|
}
|
|
|
|
|
@@ -5162,40 +5190,38 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
const ImVec2 draw_scroll = ImVec2(state->Scroll.x, 0.0f);
|
|
|
if (render_selection)
|
|
|
{
|
|
|
- const ImWchar* text_selected_begin = text_begin + ImMin(state->Stb->select_start, state->Stb->select_end);
|
|
|
- const ImWchar* text_selected_end = text_begin + ImMax(state->Stb->select_start, state->Stb->select_end);
|
|
|
+ const char* text_selected_begin = text_begin + ImMin(state->Stb->select_start, state->Stb->select_end);
|
|
|
+ const char* text_selected_end = text_begin + ImMax(state->Stb->select_start, state->Stb->select_end);
|
|
|
|
|
|
ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg, render_cursor ? 1.0f : 0.6f); // FIXME: current code flow mandate that render_cursor is always true here, we are leaving the transparent one for tests.
|
|
|
float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection.
|
|
|
float bg_offy_dn = is_multiline ? 0.0f : 2.0f;
|
|
|
ImVec2 rect_pos = draw_pos + select_start_offset - draw_scroll;
|
|
|
- for (const ImWchar* p = text_selected_begin; p < text_selected_end; )
|
|
|
+ for (const char* p = text_selected_begin; p < text_selected_end; )
|
|
|
{
|
|
|
if (rect_pos.y > clip_rect.w + g.FontSize)
|
|
|
break;
|
|
|
if (rect_pos.y < clip_rect.y)
|
|
|
{
|
|
|
- //p = (const ImWchar*)wmemchr((const wchar_t*)p, '\n', text_selected_end - p); // FIXME-OPT: Could use this when wchar_t are 16-bit
|
|
|
- //p = p ? p + 1 : text_selected_end;
|
|
|
- while (p < text_selected_end)
|
|
|
- if (*p++ == '\n')
|
|
|
- break;
|
|
|
+ p = (const char*)memchr((void*)p, '\n', text_selected_end - p);
|
|
|
+ p = p ? p + 1 : text_selected_end;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- ImVec2 rect_size = InputTextCalcTextSizeW(&g, p, text_selected_end, &p, NULL, true);
|
|
|
+ ImVec2 rect_size = InputTextCalcTextSize(&g, p, text_selected_end, &p, NULL, true);
|
|
|
if (rect_size.x <= 0.0f) rect_size.x = IM_TRUNC(g.Font->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines
|
|
|
ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos + ImVec2(rect_size.x, bg_offy_dn));
|
|
|
rect.ClipWith(clip_rect);
|
|
|
if (rect.Overlaps(clip_rect))
|
|
|
draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
|
|
|
+ rect_pos.x = draw_pos.x - draw_scroll.x;
|
|
|
}
|
|
|
- rect_pos.x = draw_pos.x - draw_scroll.x;
|
|
|
rect_pos.y += g.FontSize;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// We test for 'buf_display_max_length' as a way to avoid some pathological cases (e.g. single-line 1 MB string) which would make ImDrawList crash.
|
|
|
+ // FIXME-OPT: Multiline could submit a smaller amount of contents to AddText() since we already iterated through it.
|
|
|
if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length)
|
|
|
{
|
|
|
ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text);
|
|
@@ -5289,7 +5315,7 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state)
|
|
|
ImStb::StbUndoState* undo_state = &stb_state->undostate;
|
|
|
Text("ID: 0x%08X, ActiveID: 0x%08X", state->ID, g.ActiveId);
|
|
|
DebugLocateItemOnHover(state->ID);
|
|
|
- Text("CurLenW: %d, CurLenA: %d, Cursor: %d, Selection: %d..%d", state->CurLenW, state->CurLenA, stb_state->cursor, stb_state->select_start, stb_state->select_end);
|
|
|
+ Text("CurLenA: %d, Cursor: %d, Selection: %d..%d", state->CurLenA, stb_state->cursor, stb_state->select_start, stb_state->select_end);
|
|
|
Text("has_preferred_x: %d (%.2f)", stb_state->has_preferred_x, stb_state->preferred_x);
|
|
|
Text("undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point);
|
|
|
if (BeginChild("undopoints", ImVec2(0.0f, GetTextLineHeight() * 10), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY)) // Visualize undo state
|
|
@@ -5301,11 +5327,10 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state)
|
|
|
const char undo_rec_type = (n < undo_state->undo_point) ? 'u' : (n >= undo_state->redo_point) ? 'r' : ' ';
|
|
|
if (undo_rec_type == ' ')
|
|
|
BeginDisabled();
|
|
|
- char buf[64] = "";
|
|
|
- if (undo_rec_type != ' ' && undo_rec->char_storage != -1)
|
|
|
- ImTextStrToUtf8(buf, IM_ARRAYSIZE(buf), undo_state->undo_char + undo_rec->char_storage, undo_state->undo_char + undo_rec->char_storage + undo_rec->insert_length);
|
|
|
- Text("%c [%02d] where %03d, insert %03d, delete %03d, char_storage %03d \"%s\"",
|
|
|
- undo_rec_type, n, undo_rec->where, undo_rec->insert_length, undo_rec->delete_length, undo_rec->char_storage, buf);
|
|
|
+ const int buf_preview_len = (undo_rec_type != ' ' && undo_rec->char_storage != -1) ? undo_rec->insert_length : 0;
|
|
|
+ const char* buf_preview_str = undo_state->undo_char + undo_rec->char_storage;
|
|
|
+ Text("%c [%02d] where %03d, insert %03d, delete %03d, char_storage %03d \"%.*s\"",
|
|
|
+ undo_rec_type, n, undo_rec->where, undo_rec->insert_length, undo_rec->delete_length, undo_rec->char_storage, buf_preview_len, buf_preview_str);
|
|
|
if (undo_rec_type == ' ')
|
|
|
EndDisabled();
|
|
|
}
|
|
@@ -7385,6 +7410,7 @@ static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSe
|
|
|
|
|
|
static ImRect CalcScopeRect(ImGuiMultiSelectTempData* ms, ImGuiWindow* window)
|
|
|
{
|
|
|
+ ImGuiContext& g = *GImGui;
|
|
|
if (ms->Flags & ImGuiMultiSelectFlags_ScopeRect)
|
|
|
{
|
|
|
// Warning: this depends on CursorMaxPos so it means to be called by EndMultiSelect() only
|
|
@@ -7392,8 +7418,12 @@ static ImRect CalcScopeRect(ImGuiMultiSelectTempData* ms, ImGuiWindow* window)
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- // Add inner table decoration (#7821) // FIXME: Why not baking in InnerClipRect?
|
|
|
+ // When a table, pull HostClipRect, which allows us to predict ClipRect before first row/layout is performed. (#7970)
|
|
|
ImRect scope_rect = window->InnerClipRect;
|
|
|
+ if (g.CurrentTable != NULL)
|
|
|
+ scope_rect = g.CurrentTable->HostClipRect;
|
|
|
+
|
|
|
+ // Add inner table decoration (#7821) // FIXME: Why not baking in InnerClipRect?
|
|
|
scope_rect.Min = ImMin(scope_rect.Min + ImVec2(window->DecoInnerSizeX1, window->DecoInnerSizeY1), scope_rect.Max);
|
|
|
return scope_rect;
|
|
|
}
|