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)) {