WIP insert mode (2863)
missing cursor rendering (keep caret when on carriage return)
missing storage of insert state
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index 6892d2d..190d260 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -1359,6 +1359,7 @@
static ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput;
HelpMarker("You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputTextMultiline() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example. (This is not demonstrated in imgui_demo.cpp because we don't want to include <string> in here)");
ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly);
+ ImGui::CheckboxFlags("ImGuiInputTextFlags_AlwaysOverwrite", &flags, ImGuiInputTextFlags_AlwaysOverwrite);
ImGui::CheckboxFlags("ImGuiInputTextFlags_AllowTabInput", &flags, ImGuiInputTextFlags_AllowTabInput);
ImGui::CheckboxFlags("ImGuiInputTextFlags_CtrlEnterForNewLine", &flags, ImGuiInputTextFlags_CtrlEnterForNewLine);
ImGui::InputTextMultiline("##source", text, IM_ARRAYSIZE(text), ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags);
diff --git a/imgui_internal.h b/imgui_internal.h
index 75c2905..27e5238 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1065,6 +1065,7 @@
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
bool Edited; // edited this frame
+ bool OverwriteMode; // toggle with INSERT key
ImGuiInputTextFlags Flags; // copy of InputText() flags. may be used to check if e.g. ImGuiInputTextFlags_Password is set.
ImGuiInputTextState(ImGuiContext* ctx) { memset(this, 0, sizeof(*this)); Ctx = ctx;}
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 5cbd39d..16a2562 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -3706,6 +3706,7 @@
#define STB_TEXTEDIT_K_WORDRIGHT 0x20000D // keyboard input to move cursor right one word
#define STB_TEXTEDIT_K_PGUP 0x20000E // keyboard input to move cursor up a page
#define STB_TEXTEDIT_K_PGDOWN 0x20000F // keyboard input to move cursor down a page
+#define STB_TEXTEDIT_K_INSERT 0x200010 // keyboard input to toggle insert mode
#define STB_TEXTEDIT_K_SHIFT 0x400000
#define STB_TEXTEDIT_IMPLEMENTATION
@@ -4085,9 +4086,6 @@
if (input_requested_by_tabbing || (user_clicked && io.KeyCtrl))
select_all = true;
}
-
- if (flags & ImGuiInputTextFlags_AlwaysOverwrite)
- state->Stb.insert_mode = 1; // stb field name is indeed incorrect (see #2863)
}
if (g.ActiveId != id && init_make_active)
@@ -4172,6 +4170,9 @@
state->BufCapacityA = buf_size;
state->Flags = flags;
+ // Update overwrite / insert mode
+ state->Stb.insert_mode = (flags & ImGuiInputTextFlags_AlwaysOverwrite) ? 1 : state->OverwriteMode; // stb field name is confusing (see #2863)
+
// 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.
g.ActiveIdAllowOverlap = !io.MouseDown[0];
@@ -4309,6 +4310,7 @@
else if (IsKeyPressed(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); }
else if (IsKeyPressed(ImGuiKey_PageUp) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGUP | k_mask); scroll_y -= row_count_per_page * g.FontSize; }
else if (IsKeyPressed(ImGuiKey_PageDown) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGDOWN | k_mask); scroll_y += row_count_per_page * g.FontSize; }
+ else if (IsKeyPressed(ImGuiKey_Insert) && !is_readonly) { if ((flags & ImGuiInputTextFlags_AlwaysOverwrite) == 0) { state->OnKeyPressed(STB_TEXTEDIT_K_INSERT | k_mask); state->OverwriteMode = state->Stb.insert_mode != 0; } }
else if (IsKeyPressed(ImGuiKey_Home)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); }
else if (IsKeyPressed(ImGuiKey_End)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); }
else if (IsKeyPressed(ImGuiKey_Delete) && !is_readonly && !is_cut) { state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); }
@@ -4760,30 +4762,47 @@
}
}
- // 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.
+ const ImVec2 cursor_screen_pos = draw_pos + cursor_offset - draw_scroll;
+ bool cursor_is_visible = false;
+ bool cursor_is_overwrite_mode = false;
+ if (render_cursor)
+ {
+ state->CursorAnim += io.DeltaTime;
+ cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f;
+ const bool cursor_at_eol = state->TextW[state->Stb.cursor] == 0 || state->TextW[state->Stb.cursor] == '\n';
+ cursor_is_overwrite_mode = state->Stb.insert_mode && !state->HasSelection() && !cursor_at_eol;
+ }
+
+ // Draw blinking cursor (overwrite cursor)
+ if (cursor_is_visible && cursor_is_overwrite_mode)
+ {
+ ImVec2 rect_size = InputTextCalcTextSizeW(&g, &state->TextW[state->Stb.cursor], &state->TextW[state->Stb.cursor] + 1, NULL, NULL, false);
+ ImRect cursor_rect(cursor_screen_pos.x, cursor_screen_pos.y - rect_size.y, cursor_screen_pos.x + rect_size.x, cursor_screen_pos.y);
+ if (cursor_rect.Overlaps(clip_rect))
+ draw_window->DrawList->AddRectFilled(cursor_rect.Min, cursor_rect.Max, GetColorU32(ImGuiCol_Text, 0.20f));
+ }
+
+ // Draw text 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.
if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length)
{
ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text);
draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos - draw_scroll, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect);
}
- // Draw blinking cursor
- if (render_cursor)
+ // Draw blinking cursor (append cursor)
+ if (cursor_is_visible && !cursor_is_overwrite_mode)
{
- state->CursorAnim += io.DeltaTime;
- bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f;
- ImVec2 cursor_screen_pos = ImFloor(draw_pos + cursor_offset - draw_scroll);
- ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f);
- if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
- draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text));
+ ImRect cursor_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x, cursor_screen_pos.y - 0.5f);
+ if (cursor_rect.Overlaps(clip_rect))
+ draw_window->DrawList->AddLine(cursor_rect.Min, cursor_rect.Max, GetColorU32(ImGuiCol_Text));
+ }
- // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.)
- if (!is_readonly)
- {
- g.PlatformImeData.WantVisible = true;
- g.PlatformImeData.InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize);
- g.PlatformImeData.InputLineHeight = g.FontSize;
- }
+ // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.)
+ if (cursor_is_visible && !is_readonly)
+ {
+ g.PlatformImeData.WantVisible = true;
+ g.PlatformImeData.InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize);
+ g.PlatformImeData.InputLineHeight = g.FontSize;
}
}
else
diff --git a/imstb_textedit.h b/imstb_textedit.h
index 75a159d..4020d71 100644
--- a/imstb_textedit.h
+++ b/imstb_textedit.h
@@ -740,7 +740,9 @@
if (c == '\n' && state->single_line)
break;
- if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
+ if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)
+ && (STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI]
+ ) {
stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {