Skip to content

Commit

Permalink
Added ImGuiInputTextFlags_ReadOnly flag for InputText()/InputTextMult…
Browse files Browse the repository at this point in the history
…iline() (#211)
  • Loading branch information
ocornut committed Sep 9, 2015
1 parent f5fb5f0 commit b4f1e88
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 22 deletions.
52 changes: 31 additions & 21 deletions imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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])
Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -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)
Expand Down Expand Up @@ -7271,17 +7273,23 @@ 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
{
// Apply new value immediately - copy modified buffer back
// 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)
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions imgui.h
Original file line number Diff line number Diff line change
Expand Up @@ -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()
};
Expand Down Expand Up @@ -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)
Expand Down
6 changes: 5 additions & 1 deletion imgui_demo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down

0 comments on commit b4f1e88

Please sign in to comment.