InputText: renamed namespace from stb_texture structure and added an indirection.

This commit is contained in:
ocornut 2024-09-09 16:22:55 +02:00
parent a2366f9022
commit 21d03edcb0
2 changed files with 66 additions and 51 deletions

View File

@ -216,6 +216,8 @@ namespace ImStb
} // namespace ImStb
typedef ImStb::STB_TexteditState ImStbTexteditState;
//-----------------------------------------------------------------------------
// [SECTION] Macros
//-----------------------------------------------------------------------------
@ -1110,11 +1112,13 @@ struct IMGUI_API ImGuiInputTextDeactivatedState
ImGuiInputTextDeactivatedState() { memset(this, 0, sizeof(*this)); }
void ClearFreeMemory() { ID = 0; TextA.clear(); }
};
// Internal state of the currently focused/edited text input box
// For a given item ID, access with ImGui::GetInputTextState()
struct IMGUI_API ImGuiInputTextState
{
ImGuiContext* Ctx; // parent UI context (needs to be set explicitly by parent).
ImStbTexteditState* Stb; // State for stb_textedit.h
ImGuiID ID; // widget id owning the text state
int CurLenW, CurLenA; // we need to maintain our buffer length in both UTF-8 and wchar format. UTF-8 length is valid even if TextA is not.
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.
@ -1123,7 +1127,7 @@ struct IMGUI_API ImGuiInputTextState
bool TextAIsValid; // temporary UTF8 buffer is not initially valid before we make the widget active (until then we pull the data from user argument)
int BufCapacityA; // end-user buffer capacity
ImVec2 Scroll; // horizontal offset (managed manually) + vertical scrolling (pulled from child window's own Scroll.y)
ImStb::STB_TexteditState Stb; // state for stb_textedit.h
float CursorAnim; // timer for cursor blink, reset on every user action so the cursor reappears immediately
bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!)
bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection
@ -1133,22 +1137,21 @@ struct IMGUI_API ImGuiInputTextState
int ReloadSelectionStart; // POSITIONS ARE IN IMWCHAR units *NOT* UTF-8 this is why this is not exposed yet.
int ReloadSelectionEnd;
ImGuiInputTextState() { memset(this, 0, sizeof(*this)); }
ImGuiInputTextState();
~ImGuiInputTextState();
void ClearText() { CurLenW = CurLenA = 0; TextW[0] = 0; TextA[0] = 0; CursorClamp(); }
void ClearFreeMemory() { TextW.clear(); TextA.clear(); InitialTextA.clear(); }
int GetUndoAvailCount() const { return Stb.undostate.undo_point; }
int GetRedoAvailCount() const { return IMSTB_TEXTEDIT_UNDOSTATECOUNT - Stb.undostate.redo_point; }
void OnKeyPressed(int key); // Cannot be inline because we call in code in stb_textedit.h implementation
// Cursor & Selection
void CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking
void CursorClamp() { Stb.cursor = ImMin(Stb.cursor, CurLenW); Stb.select_start = ImMin(Stb.select_start, CurLenW); Stb.select_end = ImMin(Stb.select_end, CurLenW); }
bool HasSelection() const { return Stb.select_start != Stb.select_end; }
void ClearSelection() { Stb.select_start = Stb.select_end = Stb.cursor; }
int GetCursorPos() const { return Stb.cursor; }
int GetSelectionStart() const { return Stb.select_start; }
int GetSelectionEnd() const { return Stb.select_end; }
void SelectAll() { Stb.select_start = 0; Stb.cursor = Stb.select_end = CurLenW; Stb.has_preferred_x = 0; }
void CursorClamp() { Stb->cursor = ImMin(Stb->cursor, CurLenW); Stb->select_start = ImMin(Stb->select_start, CurLenW); Stb->select_end = ImMin(Stb->select_end, CurLenW); }
bool HasSelection() const { return Stb->select_start != Stb->select_end; }
void ClearSelection() { Stb->select_start = Stb->select_end = Stb->cursor; }
int GetCursorPos() const { return Stb->cursor; }
int GetSelectionStart() const { return Stb->select_start; }
int GetSelectionEnd() const { return Stb->select_end; }
void SelectAll() { Stb->select_start = 0; Stb->cursor = Stb->select_end = CurLenW; Stb->has_preferred_x = 0; }
// Reload user buf (WIP #2890)
// If you modify underlying user-passed const char* while active you need to call this (InputText V2 may lift this)
@ -1156,7 +1159,7 @@ struct IMGUI_API ImGuiInputTextState
// if (ImGuiInputTextState* state = ImGui::GetInputTextState(id)) // id may be ImGui::GetItemID() is last item
// state->ReloadUserBufAndSelectAll();
void ReloadUserBufAndSelectAll() { ReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; }
void ReloadUserBufAndKeepSelection() { ReloadUserBuf = true; ReloadSelectionStart = Stb.select_start; ReloadSelectionEnd = Stb.select_end; }
void ReloadUserBufAndKeepSelection() { ReloadUserBuf = true; ReloadSelectionStart = Stb->select_start; ReloadSelectionEnd = Stb->select_end; }
void ReloadUserBufAndMoveToEnd() { ReloadUserBuf = true; ReloadSelectionStart = ReloadSelectionEnd = INT_MAX; }
};

View File

@ -4029,9 +4029,21 @@ static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* st
} // namespace ImStb
// We added an extra indirection where 'Stb' is heap-allocated, in order facilitate the work of bindings generators.
ImGuiInputTextState::ImGuiInputTextState()
{
memset(this, 0, sizeof(*this));
Stb = IM_NEW(ImStbTexteditState);
}
ImGuiInputTextState::~ImGuiInputTextState()
{
IM_DELETE(Stb);
}
void ImGuiInputTextState::OnKeyPressed(int key)
{
stb_textedit_key(this, &Stb, key);
stb_textedit_key(this, Stb, key);
CursorFollow = true;
CursorAnimReset();
}
@ -4229,7 +4241,7 @@ static void InputTextReconcileUndoStateAfterUserCallback(ImGuiInputTextState* st
const int insert_len = new_last_diff - first_diff + 1;
const int delete_len = old_last_diff - first_diff + 1;
if (insert_len > 0 || delete_len > 0)
if (IMSTB_TEXTEDIT_CHARTYPE* p = stb_text_createundo(&state->Stb.undostate, first_diff, delete_len, insert_len))
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);
}
@ -4371,7 +4383,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX;
const bool init_reload_from_user_buf = (state != NULL && state->ReloadUserBuf);
const bool init_changed_specs = (state != NULL && state->Stb.single_line != !is_multiline); // state != NULL means its our state.
const bool init_changed_specs = (state != NULL && state->Stb->single_line != !is_multiline); // state != NULL means its our state.
const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav);
const bool init_state = (init_make_active || user_scroll_active);
if ((init_state && g.ActiveId != id) || init_changed_specs || init_reload_from_user_buf)
@ -4417,13 +4429,13 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
else
{
state->Scroll = ImVec2(0.0f, 0.0f);
stb_textedit_initialize_state(&state->Stb, !is_multiline);
stb_textedit_initialize_state(state->Stb, !is_multiline);
}
if (init_reload_from_user_buf)
{
state->Stb.select_start = state->ReloadSelectionStart;
state->Stb.cursor = state->Stb.select_end = state->ReloadSelectionEnd;
state->Stb->select_start = state->ReloadSelectionStart;
state->Stb->cursor = state->Stb->select_end = state->ReloadSelectionEnd;
state->CursorClamp();
}
else if (!is_multiline)
@ -4437,7 +4449,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
}
if (flags & ImGuiInputTextFlags_AlwaysOverwrite)
state->Stb.insert_mode = 1; // stb field name is indeed incorrect (see #2863)
state->Stb->insert_mode = 1; // stb field name is indeed incorrect (see #2863)
}
const bool is_osx = io.ConfigMacOSXBehaviors;
@ -4545,34 +4557,34 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
}
else if (hovered && io.MouseClickedCount[0] >= 2 && !io.KeyShift)
{
stb_textedit_click(state, &state->Stb, mouse_x, mouse_y);
stb_textedit_click(state, state->Stb, mouse_x, mouse_y);
const int multiclick_count = (io.MouseClickedCount[0] - 2);
if ((multiclick_count % 2) == 0)
{
// Double-click: Select word
// We always use the "Mac" word advance for double-click select vs CTRL+Right which use the platform dependent variant:
// FIXME: There are likely many ways to improve this behavior, but there's no "right" behavior (depends on use-case, software, OS)
const bool is_bol = (state->Stb.cursor == 0) || ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb.cursor - 1) == '\n';
if (STB_TEXT_HAS_SELECTION(&state->Stb) || !is_bol)
const bool is_bol = (state->Stb->cursor == 0) || ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb->cursor - 1) == '\n';
if (STB_TEXT_HAS_SELECTION(state->Stb) || !is_bol)
state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT);
//state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT);
if (!STB_TEXT_HAS_SELECTION(&state->Stb))
ImStb::stb_textedit_prep_selection_at_cursor(&state->Stb);
state->Stb.cursor = ImStb::STB_TEXTEDIT_MOVEWORDRIGHT_MAC(state, state->Stb.cursor);
state->Stb.select_end = state->Stb.cursor;
ImStb::stb_textedit_clamp(state, &state->Stb);
if (!STB_TEXT_HAS_SELECTION(state->Stb))
ImStb::stb_textedit_prep_selection_at_cursor(state->Stb);
state->Stb->cursor = ImStb::STB_TEXTEDIT_MOVEWORDRIGHT_MAC(state, state->Stb->cursor);
state->Stb->select_end = state->Stb->cursor;
ImStb::stb_textedit_clamp(state, state->Stb);
}
else
{
// Triple-click: Select line
const bool is_eol = ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb.cursor) == '\n';
const bool is_eol = ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb->cursor) == '\n';
state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART);
state->OnKeyPressed(STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT);
state->OnKeyPressed(STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT);
if (!is_eol && is_multiline)
{
ImSwap(state->Stb.select_start, state->Stb.select_end);
state->Stb.cursor = state->Stb.select_end;
ImSwap(state->Stb->select_start, state->Stb->select_end);
state->Stb->cursor = state->Stb->select_end;
}
state->CursorFollow = false;
}
@ -4583,15 +4595,15 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (hovered)
{
if (io.KeyShift)
stb_textedit_drag(state, &state->Stb, mouse_x, mouse_y);
stb_textedit_drag(state, state->Stb, mouse_x, mouse_y);
else
stb_textedit_click(state, &state->Stb, mouse_x, mouse_y);
stb_textedit_click(state, state->Stb, mouse_x, mouse_y);
state->CursorAnimReset();
}
}
else if (io.MouseDown[0] && !state->SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f))
{
stb_textedit_drag(state, &state->Stb, mouse_x, mouse_y);
stb_textedit_drag(state, state->Stb, mouse_x, mouse_y);
state->CursorAnimReset();
state->CursorFollow = true;
}
@ -4644,7 +4656,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
IM_ASSERT(state != NULL);
const int row_count_per_page = ImMax((int)((inner_size.y - style.FramePadding.y) / g.FontSize), 1);
state->Stb.row_count_per_page = row_count_per_page;
state->Stb->row_count_per_page = row_count_per_page;
const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0);
const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl
@ -4751,8 +4763,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// Cut, Copy
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 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);
@ -4764,7 +4776,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (!state->HasSelection())
state->SelectAll();
state->CursorFollow = true;
stb_textedit_cut(state, &state->Stb);
stb_textedit_cut(state, state->Stb);
}
}
else if (is_paste)
@ -4786,7 +4798,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
clipboard_filtered[clipboard_filtered_len] = 0;
if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation
{
stb_textedit_paste(state, &state->Stb, clipboard_filtered, clipboard_filtered_len);
stb_textedit_paste(state, state->Stb, clipboard_filtered, clipboard_filtered_len);
state->CursorFollow = true;
}
MemFree(clipboard_filtered);
@ -4813,7 +4825,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
apply_new_text_length = 0;
value_changed = true;
IMSTB_TEXTEDIT_CHARTYPE empty_string;
stb_textedit_replace(state, &state->Stb, &empty_string, 0);
stb_textedit_replace(state, state->Stb, &empty_string, 0);
}
else if (strcmp(buf, state->InitialTextA.Data) != 0)
{
@ -4828,7 +4840,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
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, w_text.Data, (apply_new_text_length > 0) ? (w_text.Size - 1) : 0);
}
}
@ -4903,9 +4915,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// 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 = 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);
// Call user code
callback(&callback_data);
@ -4916,9 +4928,9 @@ 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 = 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 (buf_dirty)
{
// Callback may update buffer and thus set buf_dirty even in read-only mode.
@ -5040,13 +5052,13 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
int searches_remaining = 0;
if (render_cursor)
{
searches_input_ptr[0] = text_begin + state->Stb.cursor;
searches_input_ptr[0] = text_begin + state->Stb->cursor;
searches_result_line_no[0] = -1;
searches_remaining++;
}
if (render_selection)
{
searches_input_ptr[1] = text_begin + ImMin(state->Stb.select_start, state->Stb.select_end);
searches_input_ptr[1] = text_begin + ImMin(state->Stb->select_start, state->Stb->select_end);
searches_result_line_no[1] = -1;
searches_remaining++;
}
@ -5122,8 +5134,8 @@ 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 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);
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.
@ -5244,7 +5256,7 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state)
{
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
ImGuiContext& g = *GImGui;
ImStb::STB_TexteditState* stb_state = &state->Stb;
ImStb::STB_TexteditState* stb_state = state->Stb;
ImStb::StbUndoState* undo_state = &stb_state->undostate;
Text("ID: 0x%08X, ActiveID: 0x%08X", state->ID, g.ActiveId);
DebugLocateItemOnHover(state->ID);