Skip to content

Commit

Permalink
WIP insert mode (2863)
Browse files Browse the repository at this point in the history
missing cursor rendering (keep caret when on carriage return)
missing storage of insert state
  • Loading branch information
ocornut committed Mar 12, 2021
1 parent 287bd9b commit 30f2ca8
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 16 deletions.
1 change: 1 addition & 0 deletions imgui_demo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1272,6 +1272,7 @@ static void ShowDemoWindowWidgets()
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);
Expand Down
1 change: 1 addition & 0 deletions imgui_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,7 @@ struct IMGUI_API ImGuiInputTextState
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 UserFlags; // Temporarily set while we call user's callback
ImGuiInputTextCallback UserCallback; // "
void* UserCallbackData; // "
Expand Down
50 changes: 35 additions & 15 deletions imgui_widgets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3672,6 +3672,7 @@ static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const Im
#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
Expand Down Expand Up @@ -3980,8 +3981,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (!is_multiline && focus_requested_by_code)
select_all = true;
}
if (flags & ImGuiInputTextFlags_AlwaysOverwrite)
state->Stb.insert_mode = 1; // stb field name is indeed incorrect (see #2863)
if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl)))
select_all = true;
}
Expand Down Expand Up @@ -4064,6 +4063,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
state->UserCallback = callback;
state->UserCallbackData = callback_user_data;

// 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];
Expand Down Expand Up @@ -4164,6 +4166,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
else if (IsKeyPressedMap(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 (IsKeyPressedMap(ImGuiKey_PageUp) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGUP | k_mask); scroll_y -= row_count_per_page * g.FontSize; }
else if (IsKeyPressedMap(ImGuiKey_PageDown) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGDOWN | k_mask); scroll_y += row_count_per_page * g.FontSize; }
else if (IsKeyPressedMap(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 (IsKeyPressedMap(ImGuiKey_Home)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_End)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_Delete) && !is_readonly) { state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); }
Expand Down Expand Up @@ -4585,27 +4588,44 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
}
}

// 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(&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 = 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));

// 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.PlatformImePos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize);
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 (cursor_is_visible && !is_readonly)
g.PlatformImePos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize);
}
else
{
Expand Down
4 changes: 3 additions & 1 deletion imstb_textedit.h
Original file line number Diff line number Diff line change
Expand Up @@ -740,7 +740,9 @@ static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state,
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)) {
Expand Down

0 comments on commit 30f2ca8

Please sign in to comment.