InputText: Added support for buffer size/capacity changes via the ImGuiInputTextFlags_CallbackResize flag. (#2006, #1443, #1008).

This commit is contained in:
omar 2018-08-21 15:39:35 +02:00
parent 4de6e1f7e4
commit 24ff259816
5 changed files with 83 additions and 25 deletions

View File

@ -52,6 +52,8 @@ Other Changes:
- Window: Allow menu and popups windows from ignoring the style.WindowMinSize values so short menus/popups are not padded. (#1909) - Window: Allow menu and popups windows from ignoring the style.WindowMinSize values so short menus/popups are not padded. (#1909)
- Window: Added global io.OptResizeWindowsFromEdges option to enable resizing windows from their edges and from the lower-left corner. (#1495) - Window: Added global io.OptResizeWindowsFromEdges option to enable resizing windows from their edges and from the lower-left corner. (#1495)
- Window: Collapse button shows hovering highlight + clicking and dragging on it allows to drag the window as well. - Window: Collapse button shows hovering highlight + clicking and dragging on it allows to drag the window as well.
- InputText: Added support for buffer size/capacity changes via the ImGuiInputTextFlags_CallbackResize flag. (#2006, #1443, #1008).
- InputText: Fixed minor off-by-one issue when submitting a buffer size smaller than the initial zero-terminated buffer contents.
- Drag and Drop: Fixed an incorrect assert when dropping a source that is submitted after the target (bug introduced with 1.62 changes - Drag and Drop: Fixed an incorrect assert when dropping a source that is submitted after the target (bug introduced with 1.62 changes
related to the addition of IsItemDeactivated()). (#1875, #143) related to the addition of IsItemDeactivated()). (#1875, #143)
- Drag and Drop: Fixed ImGuiDragDropFlags_SourceNoDisableHover to affect hovering state prior to calling IsItemHovered() + fixed description. (#143) - Drag and Drop: Fixed ImGuiDragDropFlags_SourceNoDisableHover to affect hovering state prior to calling IsItemHovered() + fixed description. (#143)
@ -60,7 +62,6 @@ Other Changes:
- Drag and Drop: Payload stays available and under the mouse if the source stops being submitted, however the tooltip is replaced by "...". (#1725) - Drag and Drop: Payload stays available and under the mouse if the source stops being submitted, however the tooltip is replaced by "...". (#1725)
- Drag and Drop: Added ImGuiDragDropFlags_SourceAutoExpirePayload flag to force payload to expire if the source stops being submitted. (#1725, #143). - Drag and Drop: Added ImGuiDragDropFlags_SourceAutoExpirePayload flag to force payload to expire if the source stops being submitted. (#1725, #143).
- IsItemHovered(): Added ImGuiHoveredFlags_AllowWhenDisabled flag to query hovered status on disabled items. (#1940, #211) - IsItemHovered(): Added ImGuiHoveredFlags_AllowWhenDisabled flag to query hovered status on disabled items. (#1940, #211)
- InputText: Fixed minor off-by-one issue when submitting a buffer size smaller than the initial zero-terminated buffer contents.
- Selectable: Added ImGuiSelectableFlags_Disabled flag in the public API. (#211) - Selectable: Added ImGuiSelectableFlags_Disabled flag in the public API. (#211)
- Misc: Added IMGUI_VERSION_NUM for easy compile-time testing. (#2025) - Misc: Added IMGUI_VERSION_NUM for easy compile-time testing. (#2025)
- Misc: Added ImGuiMouseCursor_Hand cursor enum + corresponding software cursor. (#1913, 1914) [@aiekick, @ocornut] - Misc: Added ImGuiMouseCursor_Hand cursor enum + corresponding software cursor. (#1913, 1914) [@aiekick, @ocornut]

View File

@ -66,7 +66,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i
- input text: expose CursorPos in char filter event (#816) - input text: expose CursorPos in char filter event (#816)
- input text: access public fields via a non-callback API e.g. InputTextGetState("xxx") that may return NULL if not active. - input text: access public fields via a non-callback API e.g. InputTextGetState("xxx") that may return NULL if not active.
- input text: flag to disable live update of the user buffer (also applies to float/int text input) (#701) - input text: flag to disable live update of the user buffer (also applies to float/int text input) (#701)
- input text: way to dynamically grow the buffer without forcing the user to initially allocate for worse case, e.g. more natural std::string (follow up on #200)
- input text: hover tooltip could show unclamped text - input text: hover tooltip could show unclamped text
- input text: option to Tab after an Enter validation. - input text: option to Tab after an Enter validation.
- input text: add ImGuiInputTextFlags_EnterToApply? (off #218) - input text: add ImGuiInputTextFlags_EnterToApply? (off #218)

View File

@ -10535,15 +10535,23 @@ static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n)
static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len) static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len)
{ {
const bool is_resizable = (obj->UserFlags & ImGuiInputTextFlags_CallbackResize) != 0;
const int text_len = obj->CurLenW; const int text_len = obj->CurLenW;
IM_ASSERT(pos <= text_len); IM_ASSERT(pos <= text_len);
if (new_text_len + text_len + 1 > obj->Text.Size)
return false;
const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len); const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len);
if (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufCapacityA) if (!is_resizable && (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufCapacityA))
return false; return false;
// Grow internal buffer if needed
if (new_text_len + text_len + 1 > obj->Text.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);
}
ImWchar* text = obj->Text.Data; ImWchar* text = obj->Text.Data;
if (pos != text_len) if (pos != text_len)
memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar)); memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar));
@ -10585,6 +10593,11 @@ void ImGuiTextEditState::OnKeyPressed(int key)
CursorAnimReset(); CursorAnimReset();
} }
ImGuiTextEditCallbackData::ImGuiTextEditCallbackData()
{
memset(this, 0, sizeof(*this));
}
// Public API to manipulate UTF-8 text // Public API to manipulate UTF-8 text
// We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar) // We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar)
// FIXME: The existence of this rarely exercised code path is a bit of a nuisance. // FIXME: The existence of this rarely exercised code path is a bit of a nuisance.
@ -10693,6 +10706,8 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
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) 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);
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
const ImGuiIO& io = g.IO; const ImGuiIO& io = g.IO;
@ -10818,6 +10833,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
bool value_changed = false; bool value_changed = false;
bool enter_pressed = false; bool enter_pressed = false;
int backup_current_text_length = 0;
if (g.ActiveId == id) if (g.ActiveId == id)
{ {
@ -10831,7 +10847,11 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
edit_state.CursorClamp(); edit_state.CursorClamp();
} }
backup_current_text_length = edit_state.CurLenA;
edit_state.BufCapacityA = buf_size; edit_state.BufCapacityA = buf_size;
edit_state.UserFlags = flags;
edit_state.UserCallback = callback;
edit_state.UserCallbackData = callback_user_data;
// Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget.
// Down the line we should have a cleaner library-wide concept of Selected vs Active. // Down the line we should have a cleaner library-wide concept of Selected vs Active.
@ -11009,13 +11029,15 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
if (g.ActiveId == id) if (g.ActiveId == id)
{ {
const char* apply_new_text = NULL;
int apply_new_text_length = 0;
if (cancel_edit) if (cancel_edit)
{ {
// Restore initial value. Only return true if restoring to the initial value changes the current buffer contents. // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents.
if (is_editable && strncmp(buf, edit_state.InitialText.Data, buf_size) != 0) if (is_editable && strncmp(buf, edit_state.InitialText.Data, buf_size) != 0)
{ {
ImStrncpy(buf, edit_state.InitialText.Data, buf_size); apply_new_text = edit_state.InitialText.Data;
value_changed = true; apply_new_text_length = buf_size - 1;
} }
} }
@ -11100,13 +11122,40 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
} }
} }
// Copy back to user buffer // Will copy result string if modified
if (is_editable && strcmp(edit_state.TempTextBuffer.Data, buf) != 0) if (is_editable && strcmp(edit_state.TempTextBuffer.Data, buf) != 0)
{ {
ImStrncpy(buf, edit_state.TempTextBuffer.Data, edit_state.CurLenA + 1); apply_new_text = edit_state.TempTextBuffer.Data;
apply_new_text_length = edit_state.CurLenA;
}
}
// Copy result to user buffer
if (apply_new_text)
{
IM_ASSERT(apply_new_text_length >= 0);
if (backup_current_text_length != apply_new_text_length && (flags & ImGuiInputTextFlags_CallbackResize))
{
ImGuiTextEditCallbackData callback_data;
callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize;
callback_data.Flags = flags;
callback_data.Buf = buf;
callback_data.BufTextLen = edit_state.CurLenA;
callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1);
callback_data.UserData = callback_user_data;
callback(&callback_data);
buf = callback_data.Buf;
buf_size = callback_data.BufSize;
}
IM_ASSERT(apply_new_text_length <= buf_size);
ImStrncpy(buf, edit_state.TempTextBuffer.Data, apply_new_text_length + 1);
value_changed = true; value_changed = true;
} }
}
// Clear temporary user storage
edit_state.UserFlags = 0;
edit_state.UserCallback = NULL;
edit_state.UserCallbackData = NULL;
} }
// Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value) // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value)

34
imgui.h
View File

@ -643,7 +643,7 @@ enum ImGuiInputTextFlags_
ImGuiInputTextFlags_CallbackCompletion = 1 << 6, // Call user function on pressing TAB (for completion handling) ImGuiInputTextFlags_CallbackCompletion = 1 << 6, // Call user function on pressing TAB (for completion handling)
ImGuiInputTextFlags_CallbackHistory = 1 << 7, // Call user function on pressing Up/Down arrows (for history handling) ImGuiInputTextFlags_CallbackHistory = 1 << 7, // Call user function on pressing Up/Down arrows (for history handling)
ImGuiInputTextFlags_CallbackAlways = 1 << 8, // Call user function every time. User code may query cursor position, modify text buffer. ImGuiInputTextFlags_CallbackAlways = 1 << 8, // Call user function every time. User code may query cursor position, modify text buffer.
ImGuiInputTextFlags_CallbackCharFilter = 1 << 9, // Call user function to filter character. Modify data->EventChar to replace/filter input, or return 1 to discard character. ImGuiInputTextFlags_CallbackCharFilter = 1 << 9, // Call user function to filter character. Modify data->EventChar to replace/filter input, or return 1 in callback to discard character.
ImGuiInputTextFlags_AllowTabInput = 1 << 10, // Pressing TAB input a '\t' character into the text field ImGuiInputTextFlags_AllowTabInput = 1 << 10, // Pressing TAB input a '\t' character into the text field
ImGuiInputTextFlags_CtrlEnterForNewLine = 1 << 11, // In multi-line mode, unfocus with Enter, add new line with Ctrl+Enter (default is opposite: unfocus with Ctrl+Enter, add line with Enter). ImGuiInputTextFlags_CtrlEnterForNewLine = 1 << 11, // In multi-line mode, unfocus with Enter, add new line with Ctrl+Enter (default is opposite: unfocus with Ctrl+Enter, add line with Enter).
ImGuiInputTextFlags_NoHorizontalScroll = 1 << 12, // Disable following the cursor horizontally ImGuiInputTextFlags_NoHorizontalScroll = 1 << 12, // Disable following the cursor horizontally
@ -652,6 +652,7 @@ enum ImGuiInputTextFlags_
ImGuiInputTextFlags_Password = 1 << 15, // Password mode, display all characters as '*' ImGuiInputTextFlags_Password = 1 << 15, // Password mode, display all characters as '*'
ImGuiInputTextFlags_NoUndoRedo = 1 << 16, // Disable undo/redo. Note that input text owns the text data while active, if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID(). ImGuiInputTextFlags_NoUndoRedo = 1 << 16, // Disable undo/redo. Note that input text owns the text data while active, if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID().
ImGuiInputTextFlags_CharsScientific = 1 << 17, // Allow 0123456789.+-*/eE (Scientific notation input) ImGuiInputTextFlags_CharsScientific = 1 << 17, // Allow 0123456789.+-*/eE (Scientific notation input)
ImGuiInputTextFlags_CallbackResize = 1 << 18, // Allow buffer capacity resize + notify when the string wants to be resized (for string types which hold a cache of their Size)
// [Internal] // [Internal]
ImGuiInputTextFlags_Multiline = 1 << 20 // For internal use by InputTextMultiline() ImGuiInputTextFlags_Multiline = 1 << 20 // For internal use by InputTextMultiline()
}; };
@ -1415,27 +1416,30 @@ struct ImGuiStorage
}; };
// 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 to callback when a ImGuiInputTextFlags_Callback* flag is used and the corresponding callback is triggered.
// 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.
struct ImGuiTextEditCallbackData struct ImGuiTextEditCallbackData
{ {
ImGuiInputTextFlags EventFlag; // One of ImGuiInputTextFlags_Callback* // Read-only ImGuiInputTextFlags EventFlag; // One ImGuiInputTextFlags_Callback* // Read-only
ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only
void* UserData; // What user passed to InputText() // Read-only void* UserData; // What user passed to InputText() // Read-only
// CharFilter event: // Arguments for the different callback events
ImWchar EventChar; // Character input // Read-write (replace character or set to zero) // (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;
// Completion,History,Always events: ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only // [Completion,History]
// If you modify the buffer contents make sure you update 'BufTextLen' and set 'BufDirty' to true. 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!
ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only int BufTextLen; // Current text length in bytes // Read-write // [Resize,Completion,History,Always]
char* Buf; // Current text buffer // Read-write (pointed data only, can't replace the actual pointer) int BufSize; // Capacity + 1 (max text length + 1) // Read-only // [Resize,Completion,History,Always]
int BufTextLen; // Current text length in bytes // Read-write bool BufDirty; // Set if you modify Buf/BufTextLen!! // Write // [Completion,History,Always]
int BufSize; // Capacity (maximum text length + 1) // Read-only int CursorPos; // // Read-write // [Completion,History,Always]
bool BufDirty; // Set if you modify Buf/BufTextLen!! // Write int SelectionStart; // // Read-write // [Completion,History,Always] == to SelectionEnd when no selection)
int CursorPos; // // Read-write int SelectionEnd; // // Read-write // [Completion,History,Always]
int SelectionStart; // // Read-write (== to SelectionEnd when no selection)
int SelectionEnd; // // Read-write
// NB: Helper functions for text manipulation. Calling those function loses selection. // NB: Helper functions for text manipulation. Calling those function loses selection.
ImGuiTextEditCallbackData();
IMGUI_API void DeleteChars(int pos, int bytes_count); IMGUI_API void DeleteChars(int pos, int bytes_count);
IMGUI_API void InsertChars(int pos, const char* text, const char* text_end = NULL); IMGUI_API void InsertChars(int pos, const char* text, const char* text_end = NULL);
bool HasSelection() const { return SelectionStart != SelectionEnd; } bool HasSelection() const { return SelectionStart != SelectionEnd; }

View File

@ -443,6 +443,11 @@ struct IMGUI_API ImGuiTextEditState
bool CursorFollow; bool CursorFollow;
bool SelectedAllMouseLock; bool SelectedAllMouseLock;
// Temporarily set when active
ImGuiInputTextFlags UserFlags;
ImGuiTextEditCallback UserCallback;
void* UserCallbackData;
ImGuiTextEditState() { memset(this, 0, sizeof(*this)); } ImGuiTextEditState() { memset(this, 0, sizeof(*this)); }
void CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking void CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking
void CursorClamp() { StbState.cursor = ImMin(StbState.cursor, CurLenW); StbState.select_start = ImMin(StbState.select_start, CurLenW); StbState.select_end = ImMin(StbState.select_end, CurLenW); } void CursorClamp() { StbState.cursor = ImMin(StbState.cursor, CurLenW); StbState.select_start = ImMin(StbState.select_start, CurLenW); StbState.select_end = ImMin(StbState.select_end, CurLenW); }