From f4dd990e38e22b7df860ea702f15261c4ddc4b4f Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 5 Mar 2019 11:03:32 +0100 Subject: [PATCH 1/9] Comments and Issue Template --- docs/issue_template.md | 8 ++++++-- imgui.h | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/issue_template.md b/docs/issue_template.md index 73330b222..6d88b2711 100644 --- a/docs/issue_template.md +++ b/docs/issue_template.md @@ -3,12 +3,16 @@ 1. PLEASE CAREFULLY READ: https://github.com/ocornut/imgui/issues/2261 -2. IF YOU ARE HAVING AN ISSUE COMPILING/LINKING/RUNNING/DISPLAYING/ADDING FONTS, please post on the "Getting Started" Discourse forum: +2. IF YOU ARE HAVING AN ISSUE COMPILING/LINKING/RUNNING/LOADING FONTS, please post on the "Getting Started" Discourse forum: https://discourse.dearimgui.org/c/getting-started 3. PLEASE MAKE SURE that you have: read the FAQ in imgui.cpp; explored the contents of ShowDemoWindow() including the Examples menu; searched among Issues; used your IDE to search for keywords in all sources and text files; and read the link provided in (1). -4. Delete points 1-4 and PLEASE FILL THE TEMPLATE BELOW before submitting your issue. +4. Be mindful that messages are being sent to the mailbox of "Watching" users. Try to proof-read your messages before sending them. Edits are not seen by those users, unless they browse the site. + +5. Delete points 1-5 and PLEASE FILL THE TEMPLATE BELOW before submitting your issue. + +Thank you! ---- diff --git a/imgui.h b/imgui.h index eeb9b81de..52cdd06dc 100644 --- a/imgui.h +++ b/imgui.h @@ -267,7 +267,7 @@ namespace ImGui IMGUI_API float GetWindowHeight(); // get current window height (shortcut for GetWindowSize().y) IMGUI_API ImVec2 GetContentRegionMax(); // current content boundaries (typically window boundaries including scrolling, or current column boundaries), in windows coordinates IMGUI_API ImVec2 GetContentRegionAvail(); // == GetContentRegionMax() - GetCursorPos() - IMGUI_API float GetContentRegionAvailWidth(); // + IMGUI_API float GetContentRegionAvailWidth(); // == GetContentRegionAvail().x IMGUI_API ImVec2 GetWindowContentRegionMin(); // content boundaries min (roughly (0,0)-Scroll), in window coordinates IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max (roughly (0,0)+Size-Scroll) where Size can be override with SetNextWindowContentSize(), in window coordinates IMGUI_API float GetWindowContentRegionWidth(); // From ac4842fa173e4ab15db19aa0ded444fc4858154d Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 5 Mar 2019 12:03:54 +0100 Subject: [PATCH 2/9] Nav: Fixed Ctrl+Tab keeping active InputText() of a previous window active after the switch. (#2380) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1062c86e3..6335cd3b8 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -45,6 +45,7 @@ Other Changes: - Added GetBackgroundDrawList() helper to quickly get access to a ImDrawList that will be rendered behind every other windows. (#2391) - Nav: Fixed a tap on AltGR (e.g. German keyboard) from navigating to the menu layer. +- Nav: Fixed Ctrl+Tab keeping active InputText() of a previous window active after the switch. (#2380) - DragScalar, InputScalar, SliderScalar: Added support for u8/s8/u16/s16 data types. We are reusing function instances for larger types to reduce code size. (#643, #320, #708, #1011) - InputInt, InputFloat, InputScalar: Fix to keep the label of the +/- buttons centered when diff --git a/imgui.cpp b/imgui.cpp index 099c9178e..794826589 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7993,7 +7993,9 @@ static void NavUpdateWindowingHighlightWindow(int focus_change_dir) g.NavWindowingToggleLayer = false; } -// Window management mode (hold to: change focus/move/resize, tap to: toggle menu layer) +// Windowing management mode +// Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer) +// Gamepad: Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer) static void ImGui::NavUpdateWindowing() { ImGuiContext& g = *GImGui; @@ -8096,6 +8098,7 @@ static void ImGui::NavUpdateWindowing() g.NavDisableMouseHover = true; apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window); ClosePopupsOverWindow(apply_focus_window); + ClearActiveID(); FocusWindow(apply_focus_window); if (apply_focus_window->NavLastIds[0] == 0) NavInitWindow(apply_focus_window, false); From 622a27506ac473ea3a28ee3c298beffdd803c863 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 5 Mar 2019 15:23:11 +0100 Subject: [PATCH 3/9] Text: Fixed large Text/TextUnformatted call not declaring its size when starting below the lower point of the current clipping rectangle. Somehow this bug has been there since v1.0! It was hardly noticeable but would affect the scrolling range, which in turn would affect some scrolling request functions when called during the opening frame of a window. --- docs/CHANGELOG.txt | 4 +++ docs/TODO.txt | 3 +- imgui.h | 2 +- imgui_widgets.cpp | 87 ++++++++++++++++++++++------------------------ 4 files changed, 48 insertions(+), 48 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 6335cd3b8..11485779a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -63,6 +63,10 @@ Other Changes: - TabBar: Fixed a crash when using BeginTabBar() recursively (didn't affect docking). (#2371) - TabBar: Added extra mis-usage error recovery. Past the assert, common mis-usage don't lead to hard crashes any more, facilitating integration with scripting languages. (#1651) +- Text: Fixed large Text/TextUnformatted call not declaring its size when starting below the + lower point of the current clipping rectangle. Somehow this bug has been there since v1.0! + It was hardly noticeable but would affect the scrolling range, which in turn would affect + some scrolling request functions when called during the opening frame of a window. - Plot: Fixed divide-by-zero in PlotLines() when passing a count of 1. (#2387) [@Lectem] - Log/Capture: Fixed extraneous leading carriage return. - Log/Capture: Fixed an issue when empty string on a new line would not emit a carriage return. diff --git a/docs/TODO.txt b/docs/TODO.txt index d0fa97552..34f6f946d 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -191,6 +191,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - text wrapped: figure out better way to use TextWrapped() in an always auto-resize context (tooltip, etc.) (#249) - text: it's currently impossible to have a window title with "##". perhaps an official workaround would be nice. \ style inhibitor? non-visible ascii code to insert between #? - text: provided a framed text helper, e.g. https://pastebin.com/1Laxy8bT + - text: refactor TextUnformatted (or underlying function) to more explicitly request if we need width measurement or not - text link/url button: underlined. should api expose an ID or use text contents as ID? which colors enum to use? - tree node / optimization: avoid formatting when clipped. @@ -222,7 +223,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - log: let user copy any window content to clipboard easily (CTRL+C on windows? while moving it? context menu?). code is commented because it fails with multiple Begin/End pairs. - log: obsolete LogButtons() all together. - log: LogButtons() options for specifying depth and/or hiding depth slider - + - filters: set a current filter that tree node can automatically query to hide themselves - filters: handle wild-cards (with implicit leading/trailing *), reg-exprs - filters: fuzzy matches (may use code at blog.forrestthewoods.com/4cffeed33fdb) diff --git a/imgui.h b/imgui.h index 52cdd06dc..8cc826f50 100644 --- a/imgui.h +++ b/imgui.h @@ -297,7 +297,7 @@ namespace ImGui IMGUI_API void SetScrollX(float scroll_x); // set scrolling amount [0..GetScrollMaxX()] IMGUI_API void SetScrollY(float scroll_y); // set scrolling amount [0..GetScrollMaxY()] IMGUI_API void SetScrollHereY(float center_y_ratio = 0.5f); // adjust scrolling amount to make current cursor position visible. center_y_ratio=0.0: top, 0.5: center, 1.0: bottom. When using to make a "default/current item" visible, consider using SetItemDefaultFocus() instead. - IMGUI_API void SetScrollFromPosY(float local_y, float center_y_ratio = 0.5f); // adjust scrolling amount to make given position valid. use GetCursorPos() or GetCursorStartPos()+offset to get valid positions. + IMGUI_API void SetScrollFromPosY(float local_y, float center_y_ratio = 0.5f); // adjust scrolling amount to make given position visible. Generally GetCursorStartPos() + offset to compute a valid position. // Parameters stacks (shared) IMGUI_API void PushFont(ImFont* font); // use NULL as a shortcut to push default font diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index abdeca0c6..865cd8ad1 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -159,53 +159,15 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) const ImRect clip_rect = window->ClipRect; ImVec2 text_size(0,0); - if (text_pos.y <= clip_rect.Max.y) + // Lines to skip (can't skip when logging text) + ImVec2 pos = text_pos; + if (!g.LogEnabled) { - ImVec2 pos = text_pos; - - // Lines to skip (can't skip when logging text) - if (!g.LogEnabled) + int lines_skippable = (int)((clip_rect.Min.y - text_pos.y) / line_height); + if (lines_skippable > 0) { - int lines_skippable = (int)((clip_rect.Min.y - text_pos.y) / line_height); - if (lines_skippable > 0) - { - int lines_skipped = 0; - while (line < text_end && lines_skipped < lines_skippable) - { - const char* line_end = (const char*)memchr(line, '\n', text_end - line); - if (!line_end) - line_end = text_end; - line = line_end + 1; - lines_skipped++; - } - pos.y += lines_skipped * line_height; - } - } - - // Lines to render - if (line < text_end) - { - ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); - while (line < text_end) - { - if (IsClippedEx(line_rect, 0, false)) - break; - - const char* line_end = (const char*)memchr(line, '\n', text_end - line); - if (!line_end) - line_end = text_end; - const ImVec2 line_size = CalcTextSize(line, line_end, false); - text_size.x = ImMax(text_size.x, line_size.x); - RenderText(pos, line, line_end, false); - line = line_end + 1; - line_rect.Min.y += line_height; - line_rect.Max.y += line_height; - pos.y += line_height; - } - - // Count remaining lines int lines_skipped = 0; - while (line < text_end) + while (line < text_end && lines_skipped < lines_skippable) { const char* line_end = (const char*)memchr(line, '\n', text_end - line); if (!line_end) @@ -215,10 +177,43 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) } pos.y += lines_skipped * line_height; } - - text_size.y += (pos - text_pos).y; } + // Lines to render + if (line < text_end) + { + ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); + while (line < text_end) + { + if (IsClippedEx(line_rect, 0, false)) + break; + + const char* line_end = (const char*)memchr(line, '\n', text_end - line); + if (!line_end) + line_end = text_end; + const ImVec2 line_size = CalcTextSize(line, line_end, false); + text_size.x = ImMax(text_size.x, line_size.x); + RenderText(pos, line, line_end, false); + line = line_end + 1; + line_rect.Min.y += line_height; + line_rect.Max.y += line_height; + pos.y += line_height; + } + + // Count remaining lines + int lines_skipped = 0; + while (line < text_end) + { + const char* line_end = (const char*)memchr(line, '\n', text_end - line); + if (!line_end) + line_end = text_end; + line = line_end + 1; + lines_skipped++; + } + pos.y += lines_skipped * line_height; + } + text_size.y = (pos - text_pos).y; + ImRect bb(text_pos, text_pos + text_size); ItemSize(text_size); ItemAdd(bb, 0); From 1ed3c4cf4abc1278c08ecad6354de67178902eb8 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 5 Mar 2019 16:08:18 +0100 Subject: [PATCH 4/9] Internal: Text: Extracted TextUnformatted into TextEx over which we can freely atter the signature. Clarified current large text behavior of TextUnformatted with explicit ImGuiTextFlags_NoWidthForLargeClippedText flag (always set). --- docs/TODO.txt | 2 ++ imgui_internal.h | 10 +++++++++- imgui_widgets.cpp | 44 +++++++++++++++++++++++++------------------- 3 files changed, 36 insertions(+), 20 deletions(-) diff --git a/docs/TODO.txt b/docs/TODO.txt index 34f6f946d..85696c1ea 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -138,6 +138,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - image/image button: misalignment on padded/bordered button? - image/image button: parameters are confusing, image() has tint_col,border_col whereas imagebutton() has bg_col/tint_col. Even thou they are different parameters ordering could be more consistent. can we fix that? - image button: not taking an explicit id is odd. + - slider/drag: ctrl+click when format doesn't include a % character.. disable? display underlying value in default format? (see InputScalarAsWidgetReplacement) - slider: allow using the [-]/[+] buttons used by InputFloat()/InputInt() - slider: initial absolute click is imprecise. change to relative movement slider (same as scrollbar). (#1946) - slider: add dragging-based widgets to edit values with mouse (on 2 axises), saving screen real-estate. @@ -249,6 +250,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - font: finish CustomRectRegister() to allow mapping Unicode codepoint to custom texture data - font: PushFontSize API (#1018) - font: MemoryTTF taking ownership confusing/not obvious, maybe default should be opposite? + - font: storing MinAdvanceX per font would allow us to skip calculating line width (under a threshold of character count) in loops looking for block width - font/demo: add tools to show glyphs used by a text blob, display U16 value, list missing glyphs. - font/demo: demonstrate use of ImFontGlyphRangesBuilder. - font/atlas: add a missing Glyphs.reserve() diff --git a/imgui_internal.h b/imgui_internal.h index de099c481..6fcb48301 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -83,6 +83,7 @@ struct ImGuiWindowSettings; // Storage for window settings stored in .in // Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists. typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical typedef int ImGuiButtonFlags; // -> enum ImGuiButtonFlags_ // Flags: for ButtonEx(), ButtonBehavior() +typedef int ImGuiDragFlags; // -> enum ImGuiDragFlags_ // Flags: for DragBehavior() typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag() typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for DC.LastItemStatusFlags typedef int ImGuiNavHighlightFlags; // -> enum ImGuiNavHighlightFlags_ // Flags: for RenderNavHighlight() @@ -90,7 +91,7 @@ typedef int ImGuiNavDirSourceFlags; // -> enum ImGuiNavDirSourceFlags_ // Flags: typedef int ImGuiNavMoveFlags; // -> enum ImGuiNavMoveFlags_ // Flags: for navigation requests typedef int ImGuiSeparatorFlags; // -> enum ImGuiSeparatorFlags_ // Flags: for Separator() - internal typedef int ImGuiSliderFlags; // -> enum ImGuiSliderFlags_ // Flags: for SliderBehavior() -typedef int ImGuiDragFlags; // -> enum ImGuiDragFlags_ // Flags: for DragBehavior() +typedef int ImGuiTextFlags; // -> enum ImGuiTextFlags_ // Flags: for TextEx() //------------------------------------------------------------------------- // STB libraries includes @@ -381,6 +382,12 @@ enum ImGuiItemStatusFlags_ #endif }; +enum ImGuiTextFlags_ +{ + ImGuiTextFlags_None = 0, + ImGuiTextFlags_NoWidthForLargeClippedText = 1 << 0 +}; + // FIXME: this is in development, not exposed/functional as a generic feature yet. // Horizontal/Vertical enums are fixed to 0/1 so they may be used to index ImVec2 enum ImGuiLayoutType_ @@ -1501,6 +1508,7 @@ namespace ImGui IMGUI_API void RenderPixelEllipsis(ImDrawList* draw_list, ImVec2 pos, int count, ImU32 col); // Widgets + IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0); IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0,0), ImGuiButtonFlags flags = 0); IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos, float radius); IMGUI_API bool CollapseButton(ImGuiID id, const ImVec2& pos); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 865cd8ad1..6de9319af 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -132,7 +132,7 @@ static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const // - BulletTextV() //------------------------------------------------------------------------- -void ImGui::TextUnformatted(const char* text, const char* text_end) +void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -146,7 +146,7 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrentLineTextBaseOffset); const float wrap_pos_x = window->DC.TextWrapPos; - const bool wrap_enabled = wrap_pos_x >= 0.0f; + const bool wrap_enabled = (wrap_pos_x >= 0.0f); if (text_end - text > 2000 && !wrap_enabled) { // Long text! @@ -156,14 +156,13 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) // - We use memchr(), pay attention that well optimized versions of those str/mem functions are much faster than a casually written loop. const char* line = text; const float line_height = GetTextLineHeight(); - const ImRect clip_rect = window->ClipRect; ImVec2 text_size(0,0); // Lines to skip (can't skip when logging text) ImVec2 pos = text_pos; if (!g.LogEnabled) { - int lines_skippable = (int)((clip_rect.Min.y - text_pos.y) / line_height); + int lines_skippable = (int)((window->ClipRect.Min.y - text_pos.y) / line_height); if (lines_skippable > 0) { int lines_skipped = 0; @@ -172,6 +171,8 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) const char* line_end = (const char*)memchr(line, '\n', text_end - line); if (!line_end) line_end = text_end; + if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0) + text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x); line = line_end + 1; lines_skipped++; } @@ -191,8 +192,7 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) const char* line_end = (const char*)memchr(line, '\n', text_end - line); if (!line_end) line_end = text_end; - const ImVec2 line_size = CalcTextSize(line, line_end, false); - text_size.x = ImMax(text_size.x, line_size.x); + text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x); RenderText(pos, line, line_end, false); line = line_end + 1; line_rect.Min.y += line_height; @@ -207,6 +207,8 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) const char* line_end = (const char*)memchr(line, '\n', text_end - line); if (!line_end) line_end = text_end; + if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0) + text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x); line = line_end + 1; lines_skipped++; } @@ -223,7 +225,6 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f; const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width); - // Account of baseline offset ImRect bb(text_pos, text_pos + text_size); ItemSize(text_size); if (!ItemAdd(bb, 0)) @@ -234,6 +235,11 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) } } +void ImGui::TextUnformatted(const char* text, const char* text_end) +{ + TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); +} + void ImGui::Text(const char* fmt, ...) { va_list args; @@ -250,7 +256,7 @@ void ImGui::TextV(const char* fmt, va_list args) ImGuiContext& g = *GImGui; const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); - TextUnformatted(g.TempBuffer, text_end); + TextEx(g.TempBuffer, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); } void ImGui::TextColored(const ImVec4& col, const char* fmt, ...) @@ -2009,7 +2015,7 @@ bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* v, int } PopID(); - TextUnformatted(label, FindRenderedTextEnd(label)); + TextEx(label, FindRenderedTextEnd(label)); EndGroup(); return value_changed; } @@ -2052,7 +2058,7 @@ bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_cu PopItemWidth(); SameLine(0, g.Style.ItemInnerSpacing.x); - TextUnformatted(label, FindRenderedTextEnd(label)); + TextEx(label, FindRenderedTextEnd(label)); EndGroup(); PopID(); return value_changed; @@ -2097,7 +2103,7 @@ bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_ PopItemWidth(); SameLine(0, g.Style.ItemInnerSpacing.x); - TextUnformatted(label, FindRenderedTextEnd(label)); + TextEx(label, FindRenderedTextEnd(label)); EndGroup(); PopID(); @@ -2450,7 +2456,7 @@ bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, i } PopID(); - TextUnformatted(label, FindRenderedTextEnd(label)); + TextEx(label, FindRenderedTextEnd(label)); EndGroup(); return value_changed; } @@ -2749,7 +2755,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* data_p value_changed = true; } SameLine(0, style.ItemInnerSpacing.x); - TextUnformatted(label, FindRenderedTextEnd(label)); + TextEx(label, FindRenderedTextEnd(label)); style.FramePadding = backup_frame_padding; PopID(); @@ -2787,7 +2793,7 @@ bool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* v, in } PopID(); - TextUnformatted(label, FindRenderedTextEnd(label)); + TextEx(label, FindRenderedTextEnd(label)); EndGroup(); return value_changed; } @@ -4078,7 +4084,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag picker_active_window = g.CurrentWindow; if (label != label_display_end) { - TextUnformatted(label, label_display_end); + TextEx(label, label_display_end); Spacing(); } ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar; @@ -4093,7 +4099,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel)) { SameLine(0, style.ItemInnerSpacing.x); - TextUnformatted(label, label_display_end); + TextEx(label, label_display_end); } // Convert back @@ -4353,7 +4359,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl { if ((flags & ImGuiColorEditFlags_NoSidePreview)) SameLine(0, style.ItemInnerSpacing.x); - TextUnformatted(label, label_display_end); + TextEx(label, label_display_end); } } @@ -4581,7 +4587,7 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, &col, sizeof(float) * 4, ImGuiCond_Once); ColorButton(desc_id, col, flags); SameLine(); - TextUnformatted("Color"); + TextEx("Color"); EndDragDropSource(); } @@ -4622,7 +4628,7 @@ void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text; if (text_end > text) { - TextUnformatted(text, text_end); + TextEx(text, text_end); Separator(); } From ce4e62649ae99ba4b0cd14526e75cfe13e4bd3ba Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 5 Mar 2019 17:38:36 +0100 Subject: [PATCH 5/9] Internal: Tabbing: Tweaks to FocusableItemRegister and using the standard mechanism to allow/block Tab being interpreting by tabbing instead of InputText() widget. --- imgui.cpp | 21 +++++++++++++-------- imgui.h | 1 + imgui_internal.h | 7 ++++++- imgui_widgets.cpp | 5 ++++- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 794826589..d9010e94c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2895,19 +2895,21 @@ bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged return false; } -bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop) +bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id) { ImGuiContext& g = *GImGui; + // Increment counters const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0; window->FocusIdxAllCounter++; if (is_tab_stop) window->FocusIdxTabCounter++; - // Process keyboard input at this point: TAB/Shift-TAB to tab out of the currently focused item. - // Note that we can always TAB out of a widget that doesn't allow tabbing in. - if (tab_stop && (g.ActiveId == id) && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab)) - window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items. + // Process TAB/Shift-TAB to tab *OUT* of the currently focused item. + // (Note that we can always TAB out of a widget that doesn't allow tabbing in) + if (g.ActiveId == id && g.FocusTabPressed && !(g.ActiveIdBlockNavInputFlags & (1 << ImGuiNavInput_KeyTab_))) + if (window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX) + window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items. if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent) return true; @@ -3513,7 +3515,9 @@ void ImGui::NewFrame() UpdateMouseWheel(); // Pressing TAB activate widget focus - if (g.ActiveId == 0 && g.NavWindow != NULL && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab, false)) + // (This code is old and will be redesigned for Nav. In the meanwhile we use g.NavTabRequest as storage but this doesn't need ImGuiConfigFlags_NavEnableKeyboard to work!) + g.FocusTabPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab)); + if (g.ActiveId == 0 && g.FocusTabPressed) { if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX) g.NavWindow->FocusIdxTabRequestNext = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1); @@ -5167,8 +5171,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; // Prepare for item focus requests - window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1); - window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter+1)) % (window->FocusIdxTabCounter+1); + window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : ImModPositive(window->FocusIdxAllRequestNext, window->FocusIdxAllCounter + 1); + window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : ImModPositive(window->FocusIdxTabRequestNext, window->FocusIdxTabCounter + 1); window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1; window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX; @@ -7609,6 +7613,7 @@ static void ImGui::NavUpdate() NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_); NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ ); NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ ); + NAV_MAP_KEY(ImGuiKey_Tab, ImGuiNavInput_KeyTab_ ); if (g.IO.KeyCtrl) g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f; if (g.IO.KeyShift) diff --git a/imgui.h b/imgui.h index 8cc826f50..2ffe14dd8 100644 --- a/imgui.h +++ b/imgui.h @@ -967,6 +967,7 @@ enum ImGuiNavInput_ // [Internal] Don't use directly! This is used internally to differentiate keyboard from gamepad inputs for behaviors that require to differentiate them. // Keyboard behavior that have no corresponding gamepad mapping (e.g. CTRL+TAB) will be directly reading from io.KeysDown[] instead of io.NavInputs[]. ImGuiNavInput_KeyMenu_, // toggle menu // = io.KeyAlt + ImGuiNavInput_KeyTab_, // tab // = Tab key ImGuiNavInput_KeyLeft_, // move left // = Arrow keys ImGuiNavInput_KeyRight_, // move right ImGuiNavInput_KeyUp_, // move up diff --git a/imgui_internal.h b/imgui_internal.h index 6fcb48301..dc1fc08dc 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -248,6 +248,7 @@ static inline float ImLengthSqr(const ImVec4& lhs) static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = lhs.x*lhs.x + lhs.y*lhs.y; if (d > 0.0f) return 1.0f / ImSqrt(d); return fail_value; } static inline float ImFloor(float f) { return (float)(int)f; } static inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2((float)(int)v.x, (float)(int)v.y); } +static inline int ImModPositive(int a, int b) { return (a + b) % b; } static inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; } static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); } static inline float ImLinearSweep(float current, float target, float speed) { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; } @@ -891,6 +892,9 @@ struct ImGuiContext ImGuiNavMoveResult NavMoveResultLocalVisibleSet; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) ImGuiNavMoveResult NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) + // Tabbing system (older than Nav, active when Nav is disabled. Probably needs a redesign) + bool FocusTabPressed; // + // Render ImDrawData DrawData; // Main ImDrawData instance to pass render information to the user ImDrawDataBuilder DrawDataBuilder; @@ -1036,6 +1040,7 @@ struct ImGuiContext NavMoveRequestFlags = 0; NavMoveRequestForward = ImGuiNavForward_None; NavMoveDir = NavMoveDirLast = NavMoveClipDir = ImGuiDir_None; + FocusTabPressed = false; DimBgRatio = 0.0f; BackgroundDrawList._Data = &DrawListSharedData; @@ -1421,7 +1426,7 @@ namespace ImGui IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL); IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id); IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged); - IMGUI_API bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop = true); // Return true if focus is requested + IMGUI_API bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id); // Return true if focus is requested IMGUI_API void FocusableItemUnregister(ImGuiWindow* window); IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_x, float default_y); IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 6de9319af..ea0914ec1 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3274,7 +3274,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 if (g.InputTextState.ID == id) 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 = FocusableItemRegister(window, id); const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent); const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code; @@ -3337,7 +3337,10 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); + IM_ASSERT(ImGuiNavInput_COUNT < 32); g.ActiveIdBlockNavInputFlags = (1 << ImGuiNavInput_Cancel); + if (flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_AllowTabInput)) // Disable keyboard tabbing out + g.ActiveIdBlockNavInputFlags |= (1 << ImGuiNavInput_KeyTab_); if (!is_multiline && !(flags & ImGuiInputTextFlags_CallbackHistory)) g.ActiveIdAllowNavDirFlags = ((1 << ImGuiDir_Up) | (1 << ImGuiDir_Down)); } From 076be7ec4127b75af2ad88369d3d722cabbf6cd8 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 5 Mar 2019 19:00:19 +0100 Subject: [PATCH 6/9] MenuItem, Selectable: Fixed disabled widget interfering with navigation (fix c2db7f63 in 1.67). --- docs/CHANGELOG.txt | 1 + imgui_widgets.cpp | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 11485779a..4f1faee6d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -60,6 +60,7 @@ Other Changes: when manipulating the scrollbar of a multi-line input text. - ColorPicker: Fixed a bug/assertion when displaying a color picker in a collapsed window while dragging its title bar. (#2389) +- MenuItem, Selectable: Fixed disabled widget interfering with navigation (fix c2db7f63 in 1.67). - TabBar: Fixed a crash when using BeginTabBar() recursively (didn't affect docking). (#2371) - TabBar: Added extra mis-usage error recovery. Past the assert, common mis-usage don't lead to hard crashes any more, facilitating integration with scripting languages. (#1651) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index ea0914ec1..718d47642 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5179,7 +5179,20 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl bb.Min.y -= spacing_U; bb.Max.x += spacing_R; bb.Max.y += spacing_D; - if (!ItemAdd(bb, id)) + + bool item_add; + if (flags & ImGuiSelectableFlags_Disabled) + { + ImGuiItemFlags backup_item_flags = window->DC.ItemFlags; + window->DC.ItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus; + item_add = ItemAdd(bb, id); + window->DC.ItemFlags = backup_item_flags; + } + else + { + item_add = ItemAdd(bb, id); + } + if (!item_add) { if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) PushColumnClipRect(); From 9c45072cb06534ec53a8f3629f5f7090593eb1fc Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 5 Mar 2019 19:22:58 +0100 Subject: [PATCH 7/9] Demo: Added flags to InputTextMulttiline() demo. --- imgui_demo.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 080d92bd4..45a21e484 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -940,7 +940,6 @@ static void ShowDemoWindowWidgets() { // Note: we are using a fixed-sized buffer for simplicity here. See ImGuiInputTextFlags_CallbackResize // and the code in misc/cpp/imgui_stdlib.h for how to setup InputText() for dynamically resizing strings. - static bool read_only = false; static char text[1024*16] = "/*\n" " The Pentium F00F bug, shorthand for F0 0F C7 C8,\n" @@ -953,9 +952,11 @@ static void ShowDemoWindowWidgets() "label:\n" "\tlock cmpxchg8b eax\n"; + static ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput; ShowHelpMarker("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)"); - ImGui::Checkbox("Read-only", &read_only); - ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput | (read_only ? ImGuiInputTextFlags_ReadOnly : 0); + ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", (unsigned int*)&flags, ImGuiInputTextFlags_ReadOnly); + ImGui::CheckboxFlags("ImGuiInputTextFlags_AllowTabInput", (unsigned int*)&flags, ImGuiInputTextFlags_AllowTabInput); + ImGui::CheckboxFlags("ImGuiInputTextFlags_CtrlEnterForNewLine", (unsigned int*)&flags, ImGuiInputTextFlags_CtrlEnterForNewLine); ImGui::InputTextMultiline("##source", text, IM_ARRAYSIZE(text), ImVec2(-1.0f, ImGui::GetTextLineHeight() * 16), flags); ImGui::TreePop(); } From 26328fc9feded2282ff45c05f8f859285d7fd75f Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 5 Mar 2019 19:37:20 +0100 Subject: [PATCH 8/9] Internal: Tabbing/Focus: Tidying up old code, moved some state to context instead of window. Storing new data will allow us to fix the bug mentioned in #2215 (probably in next commit). --- imgui.cpp | 78 +++++++++++++++++++++++++++++------------------ imgui_internal.h | 27 +++++++++------- imgui_widgets.cpp | 2 +- 3 files changed, 65 insertions(+), 42 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d9010e94c..6d47dc8c9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2552,10 +2552,6 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) NavLastIds[0] = NavLastIds[1] = 0; NavRectRel[0] = NavRectRel[1] = ImRect(); NavLastChildNavWindow = NULL; - - FocusIdxAllCounter = FocusIdxTabCounter = -1; - FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX; - FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX; } ImGuiWindow::~ImGuiWindow() @@ -2901,22 +2897,28 @@ bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id) // Increment counters const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0; - window->FocusIdxAllCounter++; + window->DC.FocusCounterAll++; if (is_tab_stop) - window->FocusIdxTabCounter++; + window->DC.FocusCounterTab++; // Process TAB/Shift-TAB to tab *OUT* of the currently focused item. // (Note that we can always TAB out of a widget that doesn't allow tabbing in) - if (g.ActiveId == id && g.FocusTabPressed && !(g.ActiveIdBlockNavInputFlags & (1 << ImGuiNavInput_KeyTab_))) - if (window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX) - window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items. - - if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent) - return true; - if (is_tab_stop && window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent) + if (g.ActiveId == id && g.FocusTabPressed && !(g.ActiveIdBlockNavInputFlags & (1 << ImGuiNavInput_KeyTab_)) && g.FocusRequestNextWindow == NULL) { - g.NavJustTabbedId = id; - return true; + g.FocusRequestNextWindow = window; + g.FocusRequestNextCounterTab = window->DC.FocusCounterTab + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items. + } + + // Handle focus requests + if (g.FocusRequestCurrWindow == window) + { + if (window->DC.FocusCounterAll == g.FocusRequestCurrCounterAll) + return true; + if (is_tab_stop && window->DC.FocusCounterTab == g.FocusRequestCurrCounterTab) + { + g.NavJustTabbedId = id; + return true; + } } return false; @@ -2924,8 +2926,8 @@ bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id) void ImGui::FocusableItemUnregister(ImGuiWindow* window) { - window->FocusIdxAllCounter--; - window->FocusIdxTabCounter--; + window->DC.FocusCounterAll--; + window->DC.FocusCounterTab--; } ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_x, float default_y) @@ -3515,15 +3517,34 @@ void ImGui::NewFrame() UpdateMouseWheel(); // Pressing TAB activate widget focus - // (This code is old and will be redesigned for Nav. In the meanwhile we use g.NavTabRequest as storage but this doesn't need ImGuiConfigFlags_NavEnableKeyboard to work!) g.FocusTabPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab)); if (g.ActiveId == 0 && g.FocusTabPressed) { + // Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also + // manipulate the Next fields even, even though they will be turned into Curr fields by the code below. + g.FocusRequestNextWindow = g.NavWindow; + g.FocusRequestNextCounterAll = INT_MAX; if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX) - g.NavWindow->FocusIdxTabRequestNext = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1); + g.FocusRequestNextCounterTab = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1); else - g.NavWindow->FocusIdxTabRequestNext = g.IO.KeyShift ? -1 : 0; + g.FocusRequestNextCounterTab = g.IO.KeyShift ? -1 : 0; } + + // Turn queued focus request into current one + g.FocusRequestCurrWindow = NULL; + g.FocusRequestCurrCounterAll = g.FocusRequestCurrCounterTab = INT_MAX; + if (g.FocusRequestNextWindow != NULL) + { + ImGuiWindow* window = g.FocusRequestNextWindow; + g.FocusRequestCurrWindow = window; + if (g.FocusRequestNextCounterAll != INT_MAX && window->DC.FocusCounterAll != -1) + g.FocusRequestCurrCounterAll = ImModPositive(g.FocusRequestNextCounterAll, window->DC.FocusCounterAll + 1); + if (g.FocusRequestNextCounterTab != INT_MAX && window->DC.FocusCounterTab != -1) + g.FocusRequestCurrCounterTab = ImModPositive(g.FocusRequestNextCounterTab, window->DC.FocusCounterTab + 1); + g.FocusRequestNextWindow = NULL; + g.FocusRequestNextCounterAll = g.FocusRequestNextCounterTab = INT_MAX; + } + g.NavIdTabCounter = INT_MAX; // Mark all windows as not visible @@ -5170,12 +5191,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies) window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; - // Prepare for item focus requests - window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : ImModPositive(window->FocusIdxAllRequestNext, window->FocusIdxAllCounter + 1); - window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : ImModPositive(window->FocusIdxTabRequestNext, window->FocusIdxTabCounter + 1); - window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1; - window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX; - // Apply scrolling window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window, true); window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); @@ -5350,6 +5365,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.ChildWindows.resize(0); window->DC.LayoutType = ImGuiLayoutType_Vertical; window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical; + window->DC.FocusCounterAll = window->DC.FocusCounterTab = -1; window->DC.ItemFlags = parent_window ? parent_window->DC.ItemFlags : ImGuiItemFlags_Default_; window->DC.ItemWidth = window->ItemWidthDefault; window->DC.TextWrapPos = -1.0f; // disabled @@ -6462,9 +6478,11 @@ void ImGui::ActivateItem(ImGuiID id) void ImGui::SetKeyboardFocusHere(int offset) { IM_ASSERT(offset >= -1); // -1 is allowed but not below - ImGuiWindow* window = GetCurrentWindow(); - window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset; - window->FocusIdxTabRequestNext = INT_MAX; + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + g.FocusRequestNextWindow = window; + g.FocusRequestNextCounterAll = window->DC.FocusCounterAll + 1 + offset; + g.FocusRequestNextCounterTab = INT_MAX; } void ImGui::SetItemDefaultFocus() @@ -7373,7 +7391,7 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window. g.NavLayer = window->DC.NavLayerCurrent; g.NavIdIsAlive = true; - g.NavIdTabCounter = window->FocusIdxTabCounter; + g.NavIdTabCounter = window->DC.FocusCounterTab; window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position) } } diff --git a/imgui_internal.h b/imgui_internal.h index dc1fc08dc..4f5ac8841 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -892,7 +892,13 @@ struct ImGuiContext ImGuiNavMoveResult NavMoveResultLocalVisibleSet; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) ImGuiNavMoveResult NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) - // Tabbing system (older than Nav, active when Nav is disabled. Probably needs a redesign) + // Tabbing system (older than Nav, active even if Nav is disabled. FIXME-NAV: This needs a redesign!) + ImGuiWindow* FocusRequestCurrWindow; // + ImGuiWindow* FocusRequestNextWindow; // + int FocusRequestCurrCounterAll; // Any item being requested for focus, stored as an index (we on layout to be stable between the frame pressing TAB and the next frame, semi-ouch) + int FocusRequestCurrCounterTab; // Tab item being requested for focus, stored as an index + int FocusRequestNextCounterAll; // Stored for next frame + int FocusRequestNextCounterTab; // " bool FocusTabPressed; // // Render @@ -1040,6 +1046,10 @@ struct ImGuiContext NavMoveRequestFlags = 0; NavMoveRequestForward = ImGuiNavForward_None; NavMoveDir = NavMoveDirLast = NavMoveClipDir = ImGuiDir_None; + + FocusRequestCurrWindow = FocusRequestNextWindow = NULL; + FocusRequestCurrCounterAll = FocusRequestCurrCounterTab = INT_MAX; + FocusRequestNextCounterAll = FocusRequestNextCounterTab = INT_MAX; FocusTabPressed = false; DimBgRatio = 0.0f; @@ -1127,6 +1137,8 @@ struct IMGUI_API ImGuiWindowTempData ImGuiStorage* StateStorage; ImGuiLayoutType LayoutType; ImGuiLayoutType ParentLayoutType; // Layout type of parent window at the time of Begin() + int FocusCounterAll; // Counter for focus/tabbing system. Start at -1 and increase as assigned via FocusableItemRegister() (FIXME-NAV: Needs redesign) + int FocusCounterTab; // (same, but only count widgets which you can Tab through) // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings. ImGuiItemFlags ItemFlags; // == ItemFlagsStack.back() [empty == ImGuiItemFlags_Default] @@ -1162,8 +1174,10 @@ struct IMGUI_API ImGuiWindowTempData MenuBarOffset = ImVec2(0.0f, 0.0f); StateStorage = NULL; LayoutType = ParentLayoutType = ImGuiLayoutType_Vertical; - ItemWidth = 0.0f; + FocusCounterAll = FocusCounterTab = -1; + ItemFlags = ImGuiItemFlags_Default_; + ItemWidth = 0.0f; TextWrapPos = -1.0f; memset(StackSizesBackup, 0, sizeof(StackSizesBackup)); @@ -1248,15 +1262,6 @@ struct IMGUI_API ImGuiWindow ImGuiID NavLastIds[ImGuiNavLayer_COUNT]; // Last known NavId for this window, per layer (0/1) ImRect NavRectRel[ImGuiNavLayer_COUNT]; // Reference rectangle, in window relative space - // Navigation / Focus - // FIXME-NAV: Merge all this with the new Nav system, at least the request variables should be moved to ImGuiContext - int FocusIdxAllCounter; // Start at -1 and increase as assigned via FocusItemRegister() - int FocusIdxTabCounter; // (same, but only count widgets which you can Tab through) - int FocusIdxAllRequestCurrent; // Item being requested for focus - int FocusIdxTabRequestCurrent; // Tab-able item being requested for focus - int FocusIdxAllRequestNext; // Item being requested for focus, for next update (relies on layout to be stable between the frame pressing TAB and the next frame) - int FocusIdxTabRequestNext; // " - public: ImGuiWindow(ImGuiContext* context, const char* name); ~ImGuiWindow(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 718d47642..63583c34f 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3275,7 +3275,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 state = &g.InputTextState; const bool focus_requested = FocusableItemRegister(window, id); - const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent); + const bool focus_requested_by_code = focus_requested && (g.FocusRequestCurrWindow == window && g.FocusRequestCurrCounterAll == window->DC.FocusCounterAll); const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code; const bool user_clicked = hovered && io.MouseClicked[0]; From 344140004bbc3546443a546913688e810f84a1db Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 5 Mar 2019 22:09:25 +0100 Subject: [PATCH 9/9] Fixed IsItemDeactivated()/IsItemDeactivatedAfterEdit() from not correctly returning true when tabbing out of a focusable widget (Input/Slider/Drag) in most situations. (#2215, #1875) + Minor renaming of a local variable in widget code. --- docs/CHANGELOG.txt | 6 ++++-- imgui.cpp | 5 +++++ imgui_widgets.cpp | 12 ++++++------ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4f1faee6d..7f002f30b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -44,10 +44,12 @@ Other Changes: - Added GetBackgroundDrawList() helper to quickly get access to a ImDrawList that will be rendered behind every other windows. (#2391) -- Nav: Fixed a tap on AltGR (e.g. German keyboard) from navigating to the menu layer. -- Nav: Fixed Ctrl+Tab keeping active InputText() of a previous window active after the switch. (#2380) - DragScalar, InputScalar, SliderScalar: Added support for u8/s8/u16/s16 data types. We are reusing function instances for larger types to reduce code size. (#643, #320, #708, #1011) +- Nav: Fixed a tap on AltGR (e.g. German keyboard) from navigating to the menu layer. +- Nav: Fixed Ctrl+Tab keeping active InputText() of a previous window active after the switch. (#2380) +- Fixed IsItemDeactivated()/IsItemDeactivatedAfterEdit() from not correctly returning true + when tabbing out of a focusable widget (Input/Slider/Drag) in most situations. (#2215, #1875) - InputInt, InputFloat, InputScalar: Fix to keep the label of the +/- buttons centered when style.FramePadding.x is abnormally larger than style.FramePadding.y. Since the buttons are meant to be square (to align with e.g. color button) we always use FramePadding.y. (#2367) diff --git a/imgui.cpp b/imgui.cpp index 6d47dc8c9..4805c6731 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2891,6 +2891,7 @@ bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged return false; } +// Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out. bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id) { ImGuiContext& g = *GImGui; @@ -2919,6 +2920,10 @@ bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id) g.NavJustTabbedId = id; return true; } + + // If another item is about to be focused, we clear our own active id + if (g.ActiveId == id) + ClearActiveID(); } return false; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 63583c34f..a36836131 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1950,14 +1950,14 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* v, floa // Tabbing or CTRL-clicking on Drag turns it into an input box bool start_text_input = false; - const bool tab_focus_requested = FocusableItemRegister(window, id); - if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] || g.IO.MouseDoubleClicked[0])) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id)) + const bool focus_requested = FocusableItemRegister(window, id); + if (focus_requested || (hovered && (g.IO.MouseClicked[0] || g.IO.MouseDoubleClicked[0])) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id)) { SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); - if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0] || g.NavInputId == id) + if (focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0] || g.NavInputId == id) { start_text_input = true; g.ScalarAsInputTextId = 0; @@ -2385,15 +2385,15 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* v, co // Tabbing or CTRL-clicking on Slider turns it into an input box bool start_text_input = false; - const bool tab_focus_requested = FocusableItemRegister(window, id); + const bool focus_requested = FocusableItemRegister(window, id); const bool hovered = ItemHoverable(frame_bb, id); - if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id)) + if (focus_requested || (hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id)) { SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); - if (tab_focus_requested || g.IO.KeyCtrl || g.NavInputId == id) + if (focus_requested || g.IO.KeyCtrl || g.NavInputId == id) { start_text_input = true; g.ScalarAsInputTextId = 0;