Last active
July 30, 2018 02:15
-
-
Save JSandusky/47cf5397dbe48dc68e5ce8a19a3ae1ac to your computer and use it in GitHub Desktop.
ImGui::InputText with caret at end
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) | |
{ | |
ImGuiWindow* window = GetCurrentWindow(); | |
if (window->SkipItems) | |
return false; | |
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) | |
ImGuiContext& g = *GImGui; | |
const ImGuiIO& io = g.IO; | |
const ImGuiStyle& style = g.Style; | |
const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0; | |
const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0; | |
const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0; | |
const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0; | |
if (is_multiline) // Open group before calling GetID() because groups tracks id created during their spawn | |
BeginGroup(); | |
const ImGuiID id = window->GetID(label); | |
const ImVec2 label_size = CalcTextSize(label, NULL, true); | |
ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? GetTextLineHeight() * 8.0f : label_size.y) + style.FramePadding.y*2.0f); // Arbitrary default of 8 lines high for multi-line | |
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size); | |
const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? (style.ItemInnerSpacing.x + label_size.x) : 0.0f, 0.0f)); | |
ImGuiWindow* draw_window = window; | |
if (is_multiline) | |
{ | |
if (!BeginChildFrame(id, frame_bb.GetSize())) | |
{ | |
EndChildFrame(); | |
EndGroup(); | |
return false; | |
} | |
draw_window = GetCurrentWindow(); | |
size.x -= draw_window->ScrollbarSizes.x; | |
} | |
else | |
{ | |
ItemSize(total_bb, style.FramePadding.y); | |
if (!ItemAdd(total_bb, id)) | |
return false; | |
} | |
const bool hovered = ItemHoverable(frame_bb, id); | |
if (hovered) | |
g.MouseCursor = ImGuiMouseCursor_TextInput; | |
// Password pushes a temporary font with only a fallback glyph | |
if (is_password) | |
{ | |
const ImFontGlyph* glyph = g.Font->FindGlyph('*'); | |
ImFont* password_font = &g.InputTextPasswordFont; | |
password_font->FontSize = g.Font->FontSize; | |
password_font->Scale = g.Font->Scale; | |
password_font->DisplayOffset = g.Font->DisplayOffset; | |
password_font->Ascent = g.Font->Ascent; | |
password_font->Descent = g.Font->Descent; | |
password_font->ContainerAtlas = g.Font->ContainerAtlas; | |
password_font->FallbackGlyph = glyph; | |
password_font->FallbackAdvanceX = glyph->AdvanceX; | |
IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexAdvanceX.empty() && password_font->IndexLookup.empty()); | |
PushFont(password_font); | |
} | |
// NB: we are only allowed to access 'edit_state' if we are the active widget. | |
ImGuiTextEditState& edit_state = g.InputTextState; | |
const bool focus_requested = FocusableItemRegister(window, id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 0); // Using completion callback disable keyboard tabbing | |
const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent); | |
const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code; | |
const bool user_clicked = hovered && io.MouseClicked[0]; | |
const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.Id == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY"); | |
bool clear_active_id = false; | |
bool select_all = (g.ActiveId != id) && (flags & ImGuiInputTextFlags_AutoSelectAll) != 0; | |
// JS: EDIT | |
bool bypassClick = false; | |
// END JS EDIT | |
if (focus_requested || user_clicked || user_scrolled) | |
{ | |
if (g.ActiveId != id) | |
{ | |
// Start edition | |
// Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar) | |
// From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode) | |
const int prev_len_w = edit_state.CurLenW; | |
edit_state.Text.resize(buf_size+1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data isn't NULL so it doesn't crash. | |
edit_state.InitialText.resize(buf_size+1); // UTF-8. we use +1 to make sure that .Data isn't NULL so it doesn't crash. | |
ImStrncpy(edit_state.InitialText.Data, buf, edit_state.InitialText.Size); | |
const char* buf_end = NULL; | |
edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end); | |
edit_state.CurLenA = (int)(buf_end - buf); // We can't get the result from ImFormatString() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8. | |
edit_state.CursorAnimReset(); | |
// Preserve cursor position and undo/redo stack if we come back to same widget | |
// FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar). | |
const bool recycle_state = (edit_state.Id == id) && (prev_len_w == edit_state.CurLenW); | |
if (recycle_state) | |
{ | |
// Recycle existing cursor/selection/undo stack but clamp position | |
// Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler. | |
edit_state.CursorClamp(); | |
} | |
else | |
{ | |
edit_state.Id = id; | |
edit_state.ScrollX = 0.0f; | |
stb_textedit_initialize_state(&edit_state.StbState, !is_multiline); | |
if (!is_multiline && focus_requested_by_code) | |
select_all = true; | |
} | |
// JS: EDIT | |
if (flags & ImGuiInputTextFlags_AutoCaretEnd) | |
{ | |
edit_state.StbState.cursor = edit_state.CurLenW; | |
bypassClick = true; | |
} | |
// END JS EDIT | |
if (flags & ImGuiInputTextFlags_AlwaysInsertMode) | |
edit_state.StbState.insert_mode = true; | |
if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl))) | |
select_all = true; | |
} | |
SetActiveID(id, window); | |
FocusWindow(window); | |
} | |
else if (io.MouseClicked[0]) | |
{ | |
// Release focus when we click outside | |
clear_active_id = true; | |
} | |
bool value_changed = false; | |
bool enter_pressed = false; | |
if (g.ActiveId == id) | |
{ | |
if (!is_editable && !g.ActiveIdIsJustActivated) | |
{ | |
// When read-only we always use the live data passed to the function | |
edit_state.Text.resize(buf_size+1); | |
const char* buf_end = NULL; | |
edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end); | |
edit_state.CurLenA = (int)(buf_end - buf); | |
edit_state.CursorClamp(); | |
} | |
edit_state.BufSizeA = buf_size; | |
// 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]; | |
g.WantTextInputNextFrame = 1; | |
// Edit in progress | |
const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX; | |
const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize*0.5f)); | |
const bool osx_double_click_selects_words = io.OptMacOSXBehaviors; // OS X style: Double click selects by word instead of selecting whole text | |
if (select_all || (hovered && !osx_double_click_selects_words && io.MouseDoubleClicked[0])) | |
{ | |
edit_state.SelectAll(); | |
edit_state.SelectedAllMouseLock = true; | |
} | |
else if (hovered && osx_double_click_selects_words && io.MouseDoubleClicked[0]) | |
{ | |
// Select a word only, OS X style (by simulating keystrokes) | |
edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); | |
edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); | |
} | |
// JS: EDIT | |
else if (io.MouseClicked[0] && !edit_state.SelectedAllMouseLock && !bypassClick) | |
// END JS EDIT | |
{ | |
stb_textedit_click(&edit_state, &edit_state.StbState, mouse_x, mouse_y); | |
edit_state.CursorAnimReset(); | |
} | |
... continue original source ... |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment