Browse Source

Internals: InputText: Renaming. Comments.

pull/2040/head
omar 6 years ago
parent
commit
2dc5ec95d7
  1. 1
      TODO.txt
  2. 72
      imgui.cpp
  3. 10
      imgui.h
  4. 6
      imgui_internal.h

1
TODO.txt

@ -79,6 +79,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i
- input text multi-line: line numbers? status bar? (follow up on #200)
- input text multi-line: behave better when user changes input buffer while editing is active (even though it is illegal behavior). namely, the change of buffer can create a scrollbar glitch (#725)
- input text multi-line: better horizontal scrolling support (#383, #1224)
- input text multi-line: single call to AddText() should be coarse clipped on InputTextEx() end.
- input number: optional range min/max for Input*() functions
- input number: holding [-]/[+] buttons could increase the step speed non-linearly (or user-controlled)
- input number: use mouse wheel to step up/down

72
imgui.cpp

@ -4147,9 +4147,9 @@ void ImGui::Shutdown(ImGuiContext* context)
g.DrawDataBuilder.ClearFreeMemory();
g.OverlayDrawList.ClearFreeMemory();
g.PrivateClipboard.clear();
g.InputTextState.Text.clear();
g.InputTextState.TextW.clear();
g.InputTextState.InitialText.clear();
g.InputTextState.TempTextBuffer.clear();
g.InputTextState.TempBuffer.clear();
for (int i = 0; i < g.SettingsWindows.Size; i++)
IM_DELETE(g.SettingsWindows[i].Name);
@ -10490,13 +10490,13 @@ namespace ImGuiStb
{
static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return obj->CurLenW; }
static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->Text[idx]; }
static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx) { ImWchar c = obj->Text[line_start_idx+char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; return GImGui->Font->GetCharAdvance(c) * (GImGui->FontSize / GImGui->Font->FontSize); }
static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->TextW[idx]; }
static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx+char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; return GImGui->Font->GetCharAdvance(c) * (GImGui->FontSize / GImGui->Font->FontSize); }
static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x10000 ? 0 : key; }
static ImWchar STB_TEXTEDIT_NEWLINE = '\n';
static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx)
{
const ImWchar* text = obj->Text.Data;
const ImWchar* text = obj->TextW.Data;
const ImWchar* text_remaining = NULL;
const ImVec2 size = InputTextCalcTextSizeW(text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true);
r->x0 = 0.0f;
@ -10508,10 +10508,10 @@ static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* ob
}
static bool is_separator(unsigned int c) { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; }
static int is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (is_separator( obj->Text[idx-1] ) && !is_separator( obj->Text[idx] ) ) : 1; }
static int is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (is_separator( obj->TextW[idx-1] ) && !is_separator( obj->TextW[idx] ) ) : 1; }
static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; }
#ifdef __APPLE__ // FIXME: Move setting to IO structure
static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (!is_separator( obj->Text[idx-1] ) && is_separator( obj->Text[idx] ) ) : 1; }
static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (!is_separator( obj->TextW[idx-1] ) && is_separator( obj->TextW[idx] ) ) : 1; }
static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; }
#else
static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; }
@ -10521,14 +10521,14 @@ static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx)
static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n)
{
ImWchar* dst = obj->Text.Data + pos;
ImWchar* dst = obj->TextW.Data + pos;
// We maintain our buffer length in both UTF-8 and wchar formats
obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n);
obj->CurLenW -= n;
// Offset remaining text
const ImWchar* src = obj->Text.Data + pos + n;
const ImWchar* src = obj->TextW.Data + pos + n;
while (ImWchar c = *src++)
*dst++ = c;
*dst = '\0';
@ -10545,22 +10545,22 @@ static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const Im
return false;
// Grow internal buffer if needed
if (new_text_len + text_len + 1 > obj->Text.Size)
if (new_text_len + text_len + 1 > obj->TextW.Size)
{
if (!is_resizable)
return false;
IM_ASSERT(text_len < obj->Text.Size);
obj->Text.resize(text_len + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1);
IM_ASSERT(text_len < obj->TextW.Size);
obj->TextW.resize(text_len + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1);
}
ImWchar* text = obj->Text.Data;
ImWchar* text = obj->TextW.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));
obj->CurLenW += new_text_len;
obj->CurLenA += new_text_len_utf8;
obj->Text[obj->CurLenW] = '\0';
obj->TextW[obj->CurLenW] = '\0';
return true;
}
@ -10697,7 +10697,10 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f
}
// Edit a string of text
// NB: when active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while active has no effect.
// - buf_size account for the zero-terminator, so a buf_size of 6 can hold "Hello" but not "Hello!".
// This is so we can easily call InputText() on static arrays using ARRAYSIZE() and to match
// Note that in std::string world, capacity() would omit 1 byte used by the zero-terminator.
// - When active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while the InputText is active has no effect.
// FIXME: Rather messy function partly because we are doing UTF8 > u16 > UTF8 conversions on the go to more easily handle stb_textedit calls. Ideally we should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188
bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* callback_user_data)
{
@ -10705,7 +10708,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
if (window->SkipItems)
return false;
IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys)
IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys)
IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key)
if (flags & ImGuiInputTextFlags_CallbackResize)
IM_ASSERT(callback != NULL);
@ -10790,11 +10793,11 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
// Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar)
// From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode)
const int prev_len_w = edit_state.CurLenW;
edit_state.Text.resize(buf_size+1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data isn't NULL so it doesn't crash.
edit_state.TextW.resize(buf_size+1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data isn't NULL so it doesn't crash.
edit_state.InitialText.resize(buf_size+1); // UTF-8. we use +1 to make sure that .Data isn't NULL so it doesn't crash.
ImStrncpy(edit_state.InitialText.Data, buf, buf_size);
const char* buf_end = NULL;
edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, buf_size, buf, NULL, &buf_end);
edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, buf_size, buf, NULL, &buf_end);
edit_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.
edit_state.CursorAnimReset();
@ -10841,9 +10844,9 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
if (!is_editable && !g.ActiveIdIsJustActivated)
{
// When read-only we always use the live data passed to the function
edit_state.Text.resize(buf_size+1);
edit_state.TextW.resize(buf_size+1);
const char* buf_end = NULL;
edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end);
edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, edit_state.TextW.Size, buf, NULL, &buf_end);
edit_state.CurLenA = (int)(buf_end - buf);
edit_state.CursorClamp();
}
@ -10987,9 +10990,9 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
{
const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0;
const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : edit_state.CurLenW;
edit_state.TempTextBuffer.resize((ie-ib) * 4 + 1);
ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data+ib, edit_state.Text.Data+ie);
SetClipboardText(edit_state.TempTextBuffer.Data);
edit_state.TempBuffer.resize((ie-ib) * 4 + 1);
ImTextStrToUtf8(edit_state.TempBuffer.Data, edit_state.TempBuffer.Size, edit_state.TextW.Data+ib, edit_state.TextW.Data+ie);
SetClipboardText(edit_state.TempBuffer.Data);
}
if (is_cut)
{
@ -11053,8 +11056,8 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
// FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks.
if (is_editable)
{
edit_state.TempTextBuffer.resize(edit_state.Text.Size * 4);
ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data, NULL);
edit_state.TempBuffer.resize(edit_state.TextW.Size * 4);
ImTextStrToUtf8(edit_state.TempBuffer.Data, edit_state.TempBuffer.Size, edit_state.TextW.Data, NULL);
}
// User callback
@ -11092,13 +11095,13 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
callback_data.UserData = callback_user_data;
callback_data.EventKey = event_key;
callback_data.Buf = edit_state.TempTextBuffer.Data;
callback_data.Buf = edit_state.TempBuffer.Data;
callback_data.BufTextLen = edit_state.CurLenA;
callback_data.BufSize = edit_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 = edit_state.Text.Data;
ImWchar* text = edit_state.TextW.Data;
const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor);
const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start);
const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_end);
@ -11107,7 +11110,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
callback(&callback_data);
// Read back what user may have modified
IM_ASSERT(callback_data.Buf == edit_state.TempTextBuffer.Data); // Invalid to modify those fields
IM_ASSERT(callback_data.Buf == edit_state.TempBuffer.Data); // Invalid to modify those fields
IM_ASSERT(callback_data.BufSize == edit_state.BufCapacityA);
IM_ASSERT(callback_data.Flags == flags);
if (callback_data.CursorPos != utf8_cursor_pos) edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos);
@ -11116,7 +11119,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
if (callback_data.BufDirty)
{
IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!
edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, callback_data.Buf, NULL);
edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, edit_state.TextW.Size, callback_data.Buf, NULL);
edit_state.CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen()
edit_state.CursorAnimReset();
}
@ -11124,9 +11127,9 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
}
// Will copy result string if modified
if (is_editable && strcmp(edit_state.TempTextBuffer.Data, buf) != 0)
if (is_editable && strcmp(edit_state.TempBuffer.Data, buf) != 0)
{
apply_new_text = edit_state.TempTextBuffer.Data;
apply_new_text = edit_state.TempBuffer.Data;
apply_new_text_length = edit_state.CurLenA;
}
}
@ -11149,7 +11152,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
buf_size = callback_data.BufSize;
}
IM_ASSERT(apply_new_text_length <= buf_size);
ImStrncpy(buf, edit_state.TempTextBuffer.Data, apply_new_text_length + 1);
ImStrncpy(buf, edit_state.TempBuffer.Data, apply_new_text_length + 1);
value_changed = true;
}
@ -11165,7 +11168,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
// Render
// Select which buffer we are going to display. When ImGuiInputTextFlags_NoLiveEdit is set 'buf' might still be the old value. We set buf to NULL to prevent accidental usage from now on.
const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempTextBuffer.Data : buf; buf = NULL;
const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempBuffer.Data : buf; buf = NULL;
if (!is_multiline)
{
@ -11187,7 +11190,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
// - 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 = edit_state.Text.Data;
const ImWchar* text_begin = edit_state.TextW.Data;
ImVec2 cursor_offset, select_start_offset;
{
@ -11300,6 +11303,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
}
}
// FIXME-OPT: We should coarse clip very large text on this end (even though the low-level ImFont::RenderText perform line-based will do it, it will lead us to temporary vertex allocations)
draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf_display, buf_display + edit_state.CurLenA, 0.0f, is_multiline ? NULL : &clip_rect);
// Draw blinking cursor

10
imgui.h

@ -1415,11 +1415,11 @@ struct ImGuiStorage
IMGUI_API void BuildSortByKey();
};
// Shared state of InputText(), passed to callback when a ImGuiInputTextFlags_Callback* flag is used and the corresponding callback is triggered.
// Shared state of InputText(), passed as an argument to your callback when a ImGuiInputTextFlags_Callback* flag is used.
// The callback function should return 0 by default.
// Special processing:
// - ImGuiInputTextFlags_CallbackCharFilter: return 1 if the character is not allowed. You may also set 'EventChar=0' as any character replacement are allowed.
// - ImGuiInputTextFlags_CallbackResize: BufTextLen is set to the new desired string length so you can allocate or update known size. No need to initialize new characters or zero-terminator as InputText will do it.
// - ImGuiInputTextFlags_CallbackResize: BufTextLen is set to the new desired string length so you can allocate or update the size on your side of the fence. No need to initialize new characters or zero-terminator as InputText will do it.
struct ImGuiInputTextCallbackData
{
ImGuiInputTextFlags EventFlag; // One ImGuiInputTextFlags_Callback* // Read-only
@ -1430,9 +1430,9 @@ struct ImGuiInputTextCallbackData
// (If you modify the 'buf' contents make sure you update 'BufTextLen' and set 'BufDirty' to true!)
ImWchar EventChar; // Character input // Read-write // [CharFilter] Replace character or set to zero. return 1 is equivalent to setting EventChar=0;
ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only // [Completion,History]
char* Buf; // Current text buffer // Read-write // [Resize] Can replace pointer / [Completion,History,Always] Only write to pointed data, don't replace the actual pointer!
int BufTextLen; // Current text length in bytes // Read-write // [Resize,Completion,History,Always]
int BufSize; // Capacity + 1 (max text length + 1) // Read-only // [Resize,Completion,History,Always]
char* Buf; // Text buffer // Read-write // [Resize] Can replace pointer / [Completion,History,Always] Only write to pointed data, don't replace the actual pointer!
int BufTextLen; // Text length in bytes // Read-write // [Resize,Completion,History,Always] Exclude zero-terminator storage. In C land: == strlen(some_text), in C++ land: string.length()
int BufSize; // Buffer capacity in bytes // Read-only // [Resize,Completion,History,Always] Include zero-terminator storage. In C land == ARRAYSIZE(my_char_array), in C++ land: string.capacity()+1
bool BufDirty; // Set if you modify Buf/BufTextLen!! // Write // [Completion,History,Always]
int CursorPos; // // Read-write // [Completion,History,Always]
int SelectionStart; // // Read-write // [Completion,History,Always] == to SelectionEnd when no selection)

6
imgui_internal.h

@ -432,9 +432,9 @@ struct IMGUI_API ImGuiMenuColumns
struct IMGUI_API ImGuiInputTextState
{
ImGuiID ID; // widget id owning the text state
ImVector<ImWchar> Text; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer.
ImVector<ImWchar> TextW; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer.
ImVector<char> InitialText; // backup of end-user buffer at the time of focus (in UTF-8, unaltered)
ImVector<char> TempTextBuffer;
ImVector<char> TempBuffer; // temporary buffer for callback and other other operations. size=capacity.
int CurLenA, CurLenW; // we need to maintain our buffer length in both UTF-8 and wchar format.
int BufCapacityA; // end-user buffer capacity
float ScrollX;
@ -454,7 +454,7 @@ struct IMGUI_API ImGuiInputTextState
bool HasSelection() const { return StbState.select_start != StbState.select_end; }
void ClearSelection() { StbState.select_start = StbState.select_end = StbState.cursor; }
void SelectAll() { StbState.select_start = 0; StbState.cursor = StbState.select_end = CurLenW; StbState.has_preferred_x = false; }
void OnKeyPressed(int key);
void OnKeyPressed(int key); // Cannot be inline because we call in code in stb_textedit.h implementation
};
// Windows data saved in imgui.ini file

Loading…
Cancel
Save