Added ImGuiInputTextFlags_ReadOnly flag for InputText()/InputTextMultiline() (#211)

This commit is contained in:
ocornut 2015-09-09 09:08:08 +01:00
parent f5fb5f0c1e
commit b4f1e88860
3 changed files with 38 additions and 22 deletions

View File

@ -452,7 +452,8 @@
- settings: write more decent code to allow saving/loading new fields - 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 - 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: 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? - text: simple markup language for color change?
- log: LogButtons() options for specifying depth and/or hiding depth slider - 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) - 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 ImGuiID id = window->GetID(label);
const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0; 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 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 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]) if (g.IO.InputCharacters[0])
{ {
// Process text input (before we check for Return because using some IME will effectively send a Return?) // 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++) 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]) 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 (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_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_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_Delete) && is_editable) { 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_Backspace) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_Enter)) else if (IsKeyPressedMap(ImGuiKey_Enter))
{ {
bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0; 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); SetActiveID(0);
enter_pressed = true; enter_pressed = true;
} }
else // New line else if (is_editable) // New line
{ {
unsigned int c = '\n'; unsigned int c = '\n';
if (InputTextFilterCharacter(&c, flags, callback, user_data)) if (InputTextFilterCharacter(&c, flags, callback, user_data))
edit_state.OnKeyPressed((int)c); 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'; unsigned int c = '\t';
if (InputTextFilterCharacter(&c, flags, callback, user_data)) if (InputTextFilterCharacter(&c, flags, callback, user_data))
edit_state.OnKeyPressed((int)c); edit_state.OnKeyPressed((int)c);
} }
else if (IsKeyPressedMap(ImGuiKey_Escape)) { SetActiveID(0); cancel_edit = true; } 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_Z) && is_editable) { 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_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_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 (is_ctrl_only && ((IsKeyPressedMap(ImGuiKey_X) && is_editable) || IsKeyPressedMap(ImGuiKey_C)) && (!is_multiline || edit_state.HasSelection()))
{ {
// Cut, Copy // Cut, Copy
const bool cut = IsKeyPressedMap(ImGuiKey_X); 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); 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 // Paste
if (g.IO.GetClipboardTextFn) if (g.IO.GetClipboardTextFn)
@ -7271,8 +7273,11 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
if (cancel_edit) if (cancel_edit)
{ {
// Restore initial value // Restore initial value
ImFormatString(buf, buf_size, "%s", edit_state.InitialText.Data); if (is_editable)
value_changed = true; {
ImFormatString(buf, buf_size, "%s", edit_state.InitialText.Data);
value_changed = true;
}
} }
else 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 // 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: 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 // 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); if (is_editable)
ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data, NULL); {
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 // User callback
if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0) 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; ImGuiTextEditCallbackData callback_data;
callback_data.EventFlag = event_flag; 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.EventKey = event_key;
callback_data.Buf = edit_state.TempTextBuffer.Data; callback_data.Buf = edit_state.TempTextBuffer.Data;
callback_data.BufSize = edit_state.BufSizeA; callback_data.BufSize = edit_state.BufSizeA;
callback_data.BufDirty = false; 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 // We have to convert from position from wchar to UTF-8 positions
ImWchar* text = edit_state.Text.Data; 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); ImFormatString(buf, buf_size, "%s", edit_state.TempTextBuffer.Data);
value_changed = true; 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) // - 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) // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation)
// - Measure text height (for scrollbar) // - 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_begin = edit_state.Text.Data;
const ImWchar* text_end = text_begin + edit_state.CurLenW; const ImWchar* text_end = text_begin + edit_state.CurLenW;
ImVec2 cursor_offset, select_start_offset; 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)); 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 // 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. 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; edit_state.InputCursorScreenPos = cursor_screen_pos;

View File

@ -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_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_NoHorizontalScroll = 1 << 12, // Disable following the cursor horizontally
ImGuiInputTextFlags_AlwaysInsertMode = 1 << 13, // Insert mode ImGuiInputTextFlags_AlwaysInsertMode = 1 << 13, // Insert mode
ImGuiInputTextFlags_ReadOnly = 1 << 14, // Read-only mode
// [Internal] // [Internal]
ImGuiInputTextFlags_Multiline = 1 << 20 // For internal use by InputTextMultiline() ImGuiInputTextFlags_Multiline = 1 << 20 // For internal use by InputTextMultiline()
}; };
@ -939,6 +940,7 @@ struct ImGuiTextEditCallbackData
ImGuiInputTextFlags EventFlag; // One of ImGuiInputTextFlags_Callback* // Read-only ImGuiInputTextFlags EventFlag; // One of ImGuiInputTextFlags_Callback* // Read-only
ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only
void* UserData; // 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: // CharFilter event:
ImWchar EventChar; // Character input // Read-write (replace character or set to zero) ImWchar EventChar; // Character input // Read-write (replace character or set to zero)

View File

@ -422,8 +422,12 @@ void ImGui::ShowTestWindow(bool* opened)
if (ImGui::TreeNode("Multi-line Text Input")) 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"; 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(); ImGui::TreePop();
} }