Skip to content

Commit

Permalink
InputText: callback InsertChars() support resize callbacks correctly …
Browse files Browse the repository at this point in the history
…(followup to 24ff259) + fixed demo to use those functions.  (#2006, #1443, #1008).
  • Loading branch information
ocornut committed Aug 22, 2018
1 parent ea19060 commit 8d639ec
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 18 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ Other Changes:
- Metrics: Added io.MetricsRenderWindow to reflect the number of visible windows.
- Metrics: Added io.MetricsActiveAllocations, moving away from the cross-context global counters than we previously used. (#1565, #1599, #586)
- Demo: Added basic Drag and Drop demo. (#143)
- Demo: Modified the Console example to use InsertChars() in the input text callback instead of poking directly into the buffer.
Although this won't make a difference in the example itself, using InsertChars() will honor the resizing callback properly. (#2006, #1443, #1008).
- Demo: Clarified the use of IsItemHovered()/IsItemActive() right after being in the "Active, Focused, Hovered & Focused Tests" section.
- Examples: Tweaked the main.cpp of each example.
- Examples: Metal: Added Metal rendering backend. (#1929, #1873) [@warrenm]
Expand Down
29 changes: 23 additions & 6 deletions imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10622,9 +10622,23 @@ void ImGuiInputTextCallbackData::DeleteChars(int pos, int bytes_count)

void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end)
{
const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0;
const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text);
if (new_text_len + BufTextLen + 1 >= BufSize)
return;
if (new_text_len + BufTextLen >= BufSize)
{
if (!is_resizable)
return;

// Contrary to STB_TEXTEDIT_INSERTCHARS() this is working in the UTF8 buffer, hence the midly similar code (until we remove the U16 buffer alltogether!)
ImGuiContext& g = *GImGui;
ImGuiInputTextState* edit_state = &g.InputTextState;
IM_ASSERT(edit_state->ID != 0 && g.ActiveId == edit_state->ID);
IM_ASSERT(Buf == edit_state->TempBuffer.Data);
int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1;
edit_state->TempBuffer.reserve(new_buf_size + 1);
Buf = edit_state->TempBuffer.Data;
BufSize = edit_state->BufCapacityA = new_buf_size;
}

if (BufTextLen != pos)
memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos));
Expand Down Expand Up @@ -10710,8 +10724,6 @@ 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_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;
const ImGuiIO& io = g.IO;
Expand All @@ -10721,6 +10733,9 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0;
const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0;
const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0;
const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0;
if (is_resizable)
IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag!

if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope,
BeginGroup();
Expand Down Expand Up @@ -11056,7 +11071,7 @@ 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.TempBuffer.resize(edit_state.TextW.Size * 4);
edit_state.TempBuffer.resize(edit_state.TextW.Size * 4 + 1);
ImTextStrToUtf8(edit_state.TempBuffer.Data, edit_state.TempBuffer.Size, edit_state.TextW.Data, NULL);
}

Expand Down Expand Up @@ -11119,6 +11134,8 @@ 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!
if (callback_data.BufTextLen > backup_current_text_length && is_resizable)
edit_state.TextW.resize(edit_state.TextW.Size + (callback_data.BufTextLen - backup_current_text_length));
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();
Expand All @@ -11138,7 +11155,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
if (apply_new_text)
{
IM_ASSERT(apply_new_text_length >= 0);
if (backup_current_text_length != apply_new_text_length && (flags & ImGuiInputTextFlags_CallbackResize))
if (backup_current_text_length != apply_new_text_length && is_resizable)
{
ImGuiInputTextCallbackData callback_data;
callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize;
Expand Down
8 changes: 5 additions & 3 deletions imgui.h
Original file line number Diff line number Diff line change
Expand Up @@ -1419,15 +1419,16 @@ struct ImGuiStorage
// 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 the size on your side of the fence. No need to initialize new characters or zero-terminator as InputText will do it.
// - ImGuiInputTextFlags_CallbackResize: notified by InputText() when the string is resized. BufTextLen is set to the new desired string length so you can update the string size on your side of the fence. You can also replace Buf pointer if your underlying data is reallocated. No need to initialize new characters or zero-terminator as InputText will do it right after the resize callback.
struct ImGuiInputTextCallbackData
{
ImGuiInputTextFlags EventFlag; // One ImGuiInputTextFlags_Callback* // Read-only
ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only
void* UserData; // What user passed to InputText() // Read-only

// Arguments for the different callback events
// (If you modify the 'buf' contents make sure you update 'BufTextLen' and set 'BufDirty' to true!)
// - To modify the text buffer in a callback, prefer using the InsertChars() / DeleteChars() function. InsertChars() will take care of calling the resize callback if necessary.
// - If you know your edits are not going to resize the underlying buffer allocation, you may modify the contents of 'Buf[]' directly. You need to update 'BufTextLen' accordingly (0 <= BufTextLen < BufSize) and set 'BufDirty'' to true so InputText can update its internal state.
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; // Text buffer // Read-write // [Resize] Can replace pointer / [Completion,History,Always] Only write to pointed data, don't replace the actual pointer!
Expand All @@ -1438,7 +1439,8 @@ struct ImGuiInputTextCallbackData
int SelectionStart; // // Read-write // [Completion,History,Always] == to SelectionEnd when no selection)
int SelectionEnd; // // Read-write // [Completion,History,Always]

// NB: Helper functions for text manipulation. Calling those function loses selection.
// Helper functions for text manipulation.
// Use those function to benefit from the CallbackResize behaviors. Calling those function reset the selection.
ImGuiInputTextCallbackData();
IMGUI_API void DeleteChars(int pos, int bytes_count);
IMGUI_API void InsertChars(int pos, const char* text, const char* text_end = NULL);
Expand Down
18 changes: 9 additions & 9 deletions imgui_demo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@

#ifdef _MSC_VER
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#endif
#ifdef __clang__
Expand Down Expand Up @@ -2809,14 +2808,15 @@ struct ExampleAppConsole
bool reclaim_focus = false;
if (ImGui::InputText("Input", InputBuf, IM_ARRAYSIZE(InputBuf), ImGuiInputTextFlags_EnterReturnsTrue|ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_CallbackHistory, &TextEditCallbackStub, (void*)this))
{
Strtrim(InputBuf);
if (InputBuf[0])
ExecCommand(InputBuf);
strcpy(InputBuf, "");
char* s = InputBuf;
Strtrim(s);
if (s[0])
ExecCommand(s);
strcpy(s, "");
reclaim_focus = true;
}

// Demonstrate keeping focus on the input box
// Auto-focus on window apparition
ImGui::SetItemDefaultFocus();
if (reclaim_focus)
ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget
Expand Down Expand Up @@ -2959,9 +2959,9 @@ struct ExampleAppConsole
// A better implementation would preserve the data on the current input line along with cursor position.
if (prev_history_pos != HistoryPos)
{
int sz = (int)snprintf(data->Buf, (size_t)data->BufSize, "%s", (HistoryPos >= 0) ? History[HistoryPos] : "");
data->CursorPos = data->SelectionStart = data->SelectionEnd = data->BufTextLen = sz;
data->BufDirty = true;
const char* history_str = (HistoryPos >= 0) ? History[HistoryPos] : "";
data->DeleteChars(0, data->BufTextLen);
data->InsertChars(0, history_str);
}
}
}
Expand Down

0 comments on commit 8d639ec

Please sign in to comment.