From b4f1e888602a8e2ba37f1297ba04796afc22fa7f Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 9 Sep 2015 09:08:08 +0100 Subject: [PATCH] Added ImGuiInputTextFlags_ReadOnly flag for InputText()/InputTextMultiline() (#211) --- imgui.cpp | 52 ++++++++++++++++++++++++++++++-------------------- imgui.h | 2 ++ imgui_demo.cpp | 6 +++++- 3 files changed, 38 insertions(+), 22 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 30d5d505..fc68eed8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -452,7 +452,8 @@ - settings: write more decent code to allow saving/loading new fields - settings: api for per-tool simple persistent data (bool,int,float,columns sizes,etc.) in .ini file - style: store rounded corners in texture to use 1 quad per corner (filled and wireframe). so rounding have minor cost. - - style: colorbox not always square? + - style: color-box not always square? + - style: a concept of "compact style" that the end-user can easily rely on (e.g. PushStyleCompact()?) that maps that other settings? - text: simple markup language for color change? - log: LogButtons() options for specifying depth and/or hiding depth slider - log: have more control over the log scope (e.g. stop logging when leaving current tree node scope) @@ -7032,6 +7033,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 const ImGuiID id = window->GetID(label); const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0; + const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0; ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true); ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), is_multiline ? ImGui::GetTextLineHeight() * 8.0f : label_size.y); // Arbitrary default of 8 lines high for multi-line @@ -7163,7 +7165,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 if (g.IO.InputCharacters[0]) { // Process text input (before we check for Return because using some IME will effectively send a Return?) - if (!is_ctrl_down && !is_alt_down) + if (!is_ctrl_down && !is_alt_down && is_editable) { for (int n = 0; n < IM_ARRAYSIZE(g.IO.InputCharacters) && g.IO.InputCharacters[n]; n++) if (unsigned int c = (unsigned int)g.IO.InputCharacters[n]) @@ -7187,8 +7189,8 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 else if (is_multiline && IsKeyPressedMap(ImGuiKey_DownArrow)) { if (is_ctrl_down) SetWindowScrollY(draw_window, draw_window->Scroll.y + g.FontSize); else edit_state.OnKeyPressed(STB_TEXTEDIT_K_DOWN| k_mask); } else if (IsKeyPressedMap(ImGuiKey_Home)) { edit_state.OnKeyPressed(is_ctrl_down ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } else if (IsKeyPressedMap(ImGuiKey_End)) { edit_state.OnKeyPressed(is_ctrl_down ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Delete)) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Backspace)) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_Delete) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_Backspace) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); } else if (IsKeyPressedMap(ImGuiKey_Enter)) { bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0; @@ -7197,24 +7199,24 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 SetActiveID(0); enter_pressed = true; } - else // New line + else if (is_editable) // New line { unsigned int c = '\n'; if (InputTextFilterCharacter(&c, flags, callback, user_data)) edit_state.OnKeyPressed((int)c); } } - else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !is_ctrl_down && !is_shift_down && !is_alt_down) + else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !is_ctrl_down && !is_shift_down && !is_alt_down && is_editable) { unsigned int c = '\t'; if (InputTextFilterCharacter(&c, flags, callback, user_data)) edit_state.OnKeyPressed((int)c); } - else if (IsKeyPressedMap(ImGuiKey_Escape)) { SetActiveID(0); cancel_edit = true; } - else if (is_ctrl_only && IsKeyPressedMap(ImGuiKey_Z)) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_UNDO); edit_state.ClearSelection(); } - else if (is_ctrl_only && IsKeyPressedMap(ImGuiKey_Y)) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_REDO); edit_state.ClearSelection(); } - else if (is_ctrl_only && IsKeyPressedMap(ImGuiKey_A)) { edit_state.SelectAll(); edit_state.CursorFollow = true; } - else if (is_ctrl_only && (IsKeyPressedMap(ImGuiKey_X) || IsKeyPressedMap(ImGuiKey_C)) && (!is_multiline || edit_state.HasSelection())) + else if (IsKeyPressedMap(ImGuiKey_Escape)) { SetActiveID(0); cancel_edit = true; } + else if (is_ctrl_only && IsKeyPressedMap(ImGuiKey_Z) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_UNDO); edit_state.ClearSelection(); } + else if (is_ctrl_only && IsKeyPressedMap(ImGuiKey_Y) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_REDO); edit_state.ClearSelection(); } + else if (is_ctrl_only && IsKeyPressedMap(ImGuiKey_A)) { edit_state.SelectAll(); edit_state.CursorFollow = true; } + else if (is_ctrl_only && ((IsKeyPressedMap(ImGuiKey_X) && is_editable) || IsKeyPressedMap(ImGuiKey_C)) && (!is_multiline || edit_state.HasSelection())) { // Cut, Copy const bool cut = IsKeyPressedMap(ImGuiKey_X); @@ -7236,7 +7238,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 stb_textedit_cut(&edit_state, &edit_state.StbState); } } - else if (is_ctrl_only && IsKeyPressedMap(ImGuiKey_V)) + else if (is_ctrl_only && IsKeyPressedMap(ImGuiKey_V) && is_editable) { // Paste if (g.IO.GetClipboardTextFn) @@ -7271,8 +7273,11 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 if (cancel_edit) { // Restore initial value - ImFormatString(buf, buf_size, "%s", edit_state.InitialText.Data); - value_changed = true; + if (is_editable) + { + ImFormatString(buf, buf_size, "%s", edit_state.InitialText.Data); + value_changed = true; + } } else { @@ -7280,8 +7285,11 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 // Note that as soon as we can focus into the input box, the in-widget value gets priority over any underlying modification of the input buffer // FIXME: We actually always render 'buf' in RenderTextScrolledClipped // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks - edit_state.TempTextBuffer.resize(edit_state.Text.Size * 4); - ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data, NULL); + if (is_editable) + { + edit_state.TempTextBuffer.resize(edit_state.Text.Size * 4); + ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data, NULL); + } // User callback if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0) @@ -7311,12 +7319,14 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 { ImGuiTextEditCallbackData callback_data; callback_data.EventFlag = event_flag; + callback_data.Flags = flags; + callback_data.UserData = user_data; + callback_data.ReadOnly = !is_editable; + callback_data.EventKey = event_key; callback_data.Buf = edit_state.TempTextBuffer.Data; callback_data.BufSize = edit_state.BufSizeA; callback_data.BufDirty = false; - callback_data.Flags = flags; - callback_data.UserData = user_data; // We have to convert from position from wchar to UTF-8 positions ImWchar* text = edit_state.Text.Data; @@ -7343,7 +7353,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 } } - if (strcmp(edit_state.TempTextBuffer.Data, buf) != 0) + if (is_editable && strcmp(edit_state.TempTextBuffer.Data, buf) != 0) { ImFormatString(buf, buf_size, "%s", edit_state.TempTextBuffer.Data); value_changed = true; @@ -7366,7 +7376,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 // - Display the text (this can be more easily clipped) // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation) // - Measure text height (for scrollbar) - // We are attempting to do most of that in one main pass to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort) + // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort) const ImWchar* text_begin = edit_state.Text.Data; const ImWchar* text_end = text_begin + edit_state.CurLenW; ImVec2 cursor_offset, select_start_offset; @@ -7485,7 +7495,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 draw_window->DrawList->AddLine(cursor_screen_pos + ImVec2(0.0f,-g.FontSize+0.5f), cursor_screen_pos + ImVec2(0.0f,-1.5f), window->Color(ImGuiCol_Text)); // Notify OS of text input position for advanced IME - if (io.ImeSetInputScreenPosFn && ImLengthSqr(edit_state.InputCursorScreenPos - cursor_screen_pos) > 0.0001f) + if (is_editable && io.ImeSetInputScreenPosFn && ImLengthSqr(edit_state.InputCursorScreenPos - cursor_screen_pos) > 0.0001f) io.ImeSetInputScreenPosFn((int)cursor_screen_pos.x - 1, (int)(cursor_screen_pos.y - g.FontSize)); // -1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety. edit_state.InputCursorScreenPos = cursor_screen_pos; diff --git a/imgui.h b/imgui.h index bb7854e4..c8ae1caf 100644 --- a/imgui.h +++ b/imgui.h @@ -487,6 +487,7 @@ enum ImGuiInputTextFlags_ ImGuiInputTextFlags_CtrlEnterForNewLine = 1 << 11, // In multi-line mode, allow exiting edition by pressing Enter. Ctrl+Enter to add new line (by default adds new lines with Enter). ImGuiInputTextFlags_NoHorizontalScroll = 1 << 12, // Disable following the cursor horizontally ImGuiInputTextFlags_AlwaysInsertMode = 1 << 13, // Insert mode + ImGuiInputTextFlags_ReadOnly = 1 << 14, // Read-only mode // [Internal] ImGuiInputTextFlags_Multiline = 1 << 20 // For internal use by InputTextMultiline() }; @@ -939,6 +940,7 @@ struct ImGuiTextEditCallbackData ImGuiInputTextFlags EventFlag; // One of ImGuiInputTextFlags_Callback* // Read-only ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only void* UserData; // What user passed to InputText() // Read-only + bool ReadOnly; // Read-only mode // Read-only // CharFilter event: ImWchar EventChar; // Character input // Read-write (replace character or set to zero) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 740bccfe..d0234ccf 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -422,8 +422,12 @@ void ImGui::ShowTestWindow(bool* opened) if (ImGui::TreeNode("Multi-line Text Input")) { + static bool read_only = false; static char text[1024*16] = "// F00F bug\nlabel:\n\tlock cmpxchg8b eax\n"; - ImGui::InputTextMultiline("##source", text, IM_ARRAYSIZE(text), ImVec2(-1.0f, ImGui::GetTextLineHeight() * 16), ImGuiInputTextFlags_AllowTabInput); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0,0)); + ImGui::Checkbox("Read-only", &read_only); + ImGui::PopStyleVar(); + ImGui::InputTextMultiline("##source", text, IM_ARRAYSIZE(text), ImVec2(-1.0f, ImGui::GetTextLineHeight() * 16), ImGuiInputTextFlags_AllowTabInput | (read_only ? ImGuiInputTextFlags_ReadOnly : 0)); ImGui::TreePop(); }