From 32be6c064b9036341fd36e7c34a11f2dc4104f6c Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 25 Aug 2020 20:08:24 +0200 Subject: [PATCH 01/13] InputText: Fixed using ImGuiInputTextFlags_Password with InputTextMultiline(). (#3427, #3428) --- docs/CHANGELOG.txt | 2 ++ imgui_widgets.cpp | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 573b813a..156a54c2 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,6 +41,8 @@ Other Changes: - InputText: Added ImGuiInputTextFlags_CallbackEdit to modify internally owned buffer after an edit. (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active). +- InputText: Fixed using ImGuiInputTextFlags_Password with InputTextMultiline(). (#3427, #3428) + It is a rather unusual or useless combination of features but no reason it shouldn't work! - DragFloat, DragScalar: Fixed ImGuiSliderFlags_ClampOnInput not being honored in the special case where v_min == v_max. (#3361) - BeginMenuBar: Fixed minor bug where CursorPosMax gets pushed to CursorPos prior to calling BeginMenuBar(), diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 2ee4c191..59fa0918 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4505,6 +4505,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } } + if (is_password && !is_displaying_hint) + PopFont(); + if (is_multiline) { Dummy(text_size + ImVec2(0.0f, g.FontSize)); // Always add room to scroll an extra line @@ -4512,9 +4515,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ EndGroup(); } - if (is_password && !is_displaying_hint) - PopFont(); - // Log as text if (g.LogEnabled && (!is_password || is_displaying_hint)) LogRenderedText(&draw_pos, buf_display, buf_display_end); From 4448734041bfff23c86dbc68dac88e69df112536 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 26 Aug 2020 11:03:55 +0200 Subject: [PATCH 02/13] ImVector: added max_size() to facilitate usage with sol2 binding generator (#3429) --- imgui.h | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui.h b/imgui.h index 65a1e304..27ef591d 100644 --- a/imgui.h +++ b/imgui.h @@ -1395,6 +1395,7 @@ struct ImVector inline bool empty() const { return Size == 0; } inline int size() const { return Size; } inline int size_in_bytes() const { return Size * (int)sizeof(T); } + inline int max_size() const { return (~(unsigned int)0) / (int)sizeof(T); } inline int capacity() const { return Capacity; } inline T& operator[](int i) { IM_ASSERT(i < Size); return Data[i]; } inline const T& operator[](int i) const { IM_ASSERT(i < Size); return Data[i]; } From 1e8b9f84da30bda10e7a6b09341d10d77313f037 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 26 Aug 2020 11:28:35 +0200 Subject: [PATCH 03/13] Nav: Removed stateful NavMoveFromClampedRefRect and made it more explicit that nav move request from gamepad start from a clipped location. --- imgui.cpp | 14 ++++---------- imgui_internal.h | 2 -- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index bd25ed1c..c1d044c8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8746,15 +8746,9 @@ static void ImGui::NavUpdate() // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds. ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f / 10.0f, 10.0f); if (scroll_dir.x != 0.0f && window->ScrollbarX) - { SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed)); - g.NavMoveFromClampedRefRect = true; - } if (scroll_dir.y != 0.0f) - { SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed)); - g.NavMoveFromClampedRefRect = true; - } } // Reset search results @@ -8762,8 +8756,10 @@ static void ImGui::NavUpdate() g.NavMoveResultLocalVisibleSet.Clear(); g.NavMoveResultOther.Clear(); - // When we have manually scrolled (without using navigation) and NavId becomes out of bounds, we project its bounding box to the visible area to restart navigation within visible items - if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == ImGuiNavLayer_Main) + // When using gamepad, we project the reference nav bounding box into window visible area. + // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad every movements are relative + // (can't focus a visible object like we can with the mouse). + if (g.NavMoveRequest && g.NavInputSource == ImGuiInputSource_NavGamepad && g.NavLayer == ImGuiNavLayer_Main) { ImGuiWindow* window = g.NavWindow; ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1)); @@ -8774,7 +8770,6 @@ static void ImGui::NavUpdate() window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel); g.NavId = g.NavFocusScopeId = 0; } - g.NavMoveFromClampedRefRect = false; } // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items) @@ -8856,7 +8851,6 @@ static void ImGui::NavUpdateMoveResult() g.NavJustMovedToKeyMods = g.NavMoveRequestKeyMods; } SetNavIDWithRectRel(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel); - g.NavMoveFromClampedRefRect = false; } // Handle PageUp/PageDown/Home/End keys diff --git a/imgui_internal.h b/imgui_internal.h index 2cb5dfa3..6bf49337 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1204,7 +1204,6 @@ struct ImGuiContext bool NavInitRequestFromMove; ImGuiID NavInitResultId; // Init request result (first item of the window, or one for which SetItemDefaultFocus() was called) ImRect NavInitResultRectRel; // Init request result rectangle (relative to parent window) - bool NavMoveFromClampedRefRect; // Set by manual scrolling, if we scroll to a point where NavId isn't visible we reset navigation from visible items bool NavMoveRequest; // Move request for this frame ImGuiNavMoveFlags NavMoveRequestFlags; ImGuiNavForward NavMoveRequestForward; // None / ForwardQueued / ForwardActive (this is used to navigate sibling parent menus from a child menu) @@ -1390,7 +1389,6 @@ struct ImGuiContext NavInitRequest = false; NavInitRequestFromMove = false; NavInitResultId = 0; - NavMoveFromClampedRefRect = false; NavMoveRequest = false; NavMoveRequestFlags = ImGuiNavMoveFlags_None; NavMoveRequestForward = ImGuiNavForward_None; From 833eb771f24c29bf3bf1788a57bd16ead7bb9d42 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Wed, 26 Aug 2020 12:21:37 +0300 Subject: [PATCH 04/13] Nav: Fix navigation resuming on first visible item when using gamepad. In cases where navigation was requested with focused item out of view, clipping of current item rect resulted in an inverted rect, which was completely discarded and ImRect(0,0,0,0) was used as current point from which navigation scoring was calculated. IsInverted() check is completely removed as rect can no longer be inverted. Since rects are not initialized to ImRect(0,0,0,0) - old .Min.x != FLT_MAX check (which was changed in c7835dd1892ab11750cd6917b37b74e426fe13b9) is not necessary either. --- docs/CHANGELOG.txt | 7 ++++--- imgui.cpp | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 156a54c2..61600a6c 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -45,11 +45,12 @@ Other Changes: It is a rather unusual or useless combination of features but no reason it shouldn't work! - DragFloat, DragScalar: Fixed ImGuiSliderFlags_ClampOnInput not being honored in the special case where v_min == v_max. (#3361) +- Nav: Fix navigation resuming on first visible item when using gamepad. [@rokups] - BeginMenuBar: Fixed minor bug where CursorPosMax gets pushed to CursorPos prior to calling BeginMenuBar(), so e.g. calling the function at the end of a window would often add +ItemSpacing.y to scrolling range. -- TreeNode, CollapsingHeader: Made clicking on arrow toggle toggle the open state on the Mouse Down event - rather than the Mouse Down+Up sequence, even if the _OpenOnArrow flag isn't set. This is standard behavior - and amends the change done in 1.76 which only affected cases were _OpenOnArrow flag was set. +- TreeNode, CollapsingHeader: Made clicking on arrow toggle toggle the open state on the Mouse Down event + rather than the Mouse Down+Up sequence, even if the _OpenOnArrow flag isn't set. This is standard behavior + and amends the change done in 1.76 which only affected cases were _OpenOnArrow flag was set. (This is also necessary to support full multi/range-select/drag and drop operations.) - Tab Bar: Keep tab item close button visible while dragging a tab (independent of hovering state). - Metrics: Various tweaks, listing windows front-to-back, greying inactive items when possible. diff --git a/imgui.cpp b/imgui.cpp index c1d044c8..f7f49479 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8767,13 +8767,13 @@ static void ImGui::NavUpdate() { float pad = window->CalcFontSize() * 0.5f; window_rect_rel.Expand(ImVec2(-ImMin(window_rect_rel.GetWidth(), pad), -ImMin(window_rect_rel.GetHeight(), pad))); // Terrible approximation for the intent of starting navigation from first fully visible item - window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel); + window->NavRectRel[g.NavLayer].ClipWithFull(window_rect_rel); g.NavId = g.NavFocusScopeId = 0; } } // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items) - ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0); + ImRect nav_rect_rel = g.NavWindow ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0); g.NavScoringRect = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect(); g.NavScoringRect.TranslateY(nav_scoring_rect_offset_y); g.NavScoringRect.Min.x = ImMin(g.NavScoringRect.Min.x + 1.0f, g.NavScoringRect.Max.x); From 8d71bc2132b71bb3e564efa12d3829a91e00453f Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 26 Aug 2020 12:39:29 +0200 Subject: [PATCH 05/13] Internals: Nav: shallow refactor. --- imgui.cpp | 62 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f7f49479..9baba2e9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8515,7 +8515,9 @@ ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInput static void ImGui::NavUpdate() { ImGuiContext& g = *GImGui; - g.IO.WantSetMousePos = false; + ImGuiIO& io = g.IO; + + io.WantSetMousePos = false; g.NavWrapRequestWindow = NULL; g.NavWrapRequestFlags = ImGuiNavMoveFlags_None; #if 0 @@ -8524,16 +8526,16 @@ static void ImGui::NavUpdate() // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard) // (do it before we map Keyboard input!) - bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; - bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; + bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; + bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; if (nav_gamepad_active) - if (g.IO.NavInputs[ImGuiNavInput_Activate] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Input] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Cancel] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Menu] > 0.0f) + if (io.NavInputs[ImGuiNavInput_Activate] > 0.0f || io.NavInputs[ImGuiNavInput_Input] > 0.0f || io.NavInputs[ImGuiNavInput_Cancel] > 0.0f || io.NavInputs[ImGuiNavInput_Menu] > 0.0f) g.NavInputSource = ImGuiInputSource_NavGamepad; // Update Keyboard->Nav inputs mapping if (nav_keyboard_active) { - #define NAV_MAP_KEY(_KEY, _NAV_INPUT) do { if (IsKeyDown(g.IO.KeyMap[_KEY])) { g.IO.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; } } while (0) + #define NAV_MAP_KEY(_KEY, _NAV_INPUT) do { if (IsKeyDown(io.KeyMap[_KEY])) { io.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; } } while (0) NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate ); NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input ); NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel ); @@ -8541,17 +8543,17 @@ 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_ ); - if (g.IO.KeyCtrl) - g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f; - if (g.IO.KeyShift) - g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f; - if (g.IO.KeyAlt && !g.IO.KeyCtrl) // AltGR is Alt+Ctrl, also even on keyboards without AltGR we don't want Alt+Ctrl to open menu. - g.IO.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f; + if (io.KeyCtrl) + io.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f; + if (io.KeyShift) + io.NavInputs[ImGuiNavInput_TweakFast] = 1.0f; + if (io.KeyAlt && !io.KeyCtrl) // AltGR is Alt+Ctrl, also even on keyboards without AltGR we don't want Alt+Ctrl to open menu. + io.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f; #undef NAV_MAP_KEY } - memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration)); - for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++) - g.IO.NavInputsDownDuration[i] = (g.IO.NavInputs[i] > 0.0f) ? (g.IO.NavInputsDownDuration[i] < 0.0f ? 0.0f : g.IO.NavInputsDownDuration[i] + g.IO.DeltaTime) : -1.0f; + memcpy(io.NavInputsDownDurationPrev, io.NavInputsDownDuration, sizeof(io.NavInputsDownDuration)); + for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) + io.NavInputsDownDuration[i] = (io.NavInputs[i] > 0.0f) ? (io.NavInputsDownDuration[i] < 0.0f ? 0.0f : io.NavInputsDownDuration[i] + io.DeltaTime) : -1.0f; // Process navigation init request (select first/default focus) // In very rare cases g.NavWindow may be null (e.g. clearing focus after requesting an init request, which does happen when releasing Alt while clicking on void) @@ -8587,12 +8589,12 @@ static void ImGui::NavUpdate() if (g.NavMousePosDirty && g.NavIdIsAlive) { // Set mouse position given our knowledge of the navigated item position from last frame - if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) + if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) { if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow) { - g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos(); - g.IO.WantSetMousePos = true; + io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos(); + io.WantSetMousePos = true; } } g.NavMousePosDirty = false; @@ -8611,8 +8613,8 @@ static void ImGui::NavUpdate() NavUpdateWindowing(); // Set output flags for user application - g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); - g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL); + io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); + io.NavVisible = (io.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL); // Process NavCancel input (to close a popup, get back to parent, clear focus) if (IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed)) @@ -8715,7 +8717,7 @@ static void ImGui::NavUpdate() if (g.NavMoveDir != ImGuiDir_None) { g.NavMoveRequest = true; - g.NavMoveRequestKeyMods = g.IO.KeyMods; + g.NavMoveRequestKeyMods = io.KeyMods; g.NavMoveDirLast = g.NavMoveDir; } if (g.NavMoveRequest && g.NavId == 0) @@ -8733,7 +8735,7 @@ static void ImGui::NavUpdate() { // *Fallback* manual-scroll with Nav directional keys when window has no navigable item ImGuiWindow* window = g.NavWindow; - const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * g.IO.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. + const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest) { if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) @@ -8857,24 +8859,26 @@ static void ImGui::NavUpdateMoveResult() static float ImGui::NavUpdatePageUpPageDown() { ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; + if (g.NavMoveDir != ImGuiDir_None || g.NavWindow == NULL) return 0.0f; if ((g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != ImGuiNavLayer_Main) return 0.0f; ImGuiWindow* window = g.NavWindow; - const bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp); - const bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown); - const bool home_pressed = IsKeyPressed(g.IO.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home); - const bool end_pressed = IsKeyPressed(g.IO.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End); + const bool page_up_held = IsKeyDown(io.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp); + const bool page_down_held = IsKeyDown(io.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown); + const bool home_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home); + const bool end_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End); if (page_up_held != page_down_held || home_pressed != end_pressed) // If either (not both) are pressed { if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll) { // Fallback manual-scroll when window has no navigable item - if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true)) + if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true)) SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight()); - else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true)) + else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true)) SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight()); else if (home_pressed) SetScrollY(window, 0.0f); @@ -8886,14 +8890,14 @@ static float ImGui::NavUpdatePageUpPageDown() ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer]; const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight()); float nav_scoring_rect_offset_y = 0.0f; - if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true)) + if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true)) { nav_scoring_rect_offset_y = -page_offset_y; g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item) g.NavMoveClipDir = ImGuiDir_Up; g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; } - else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true)) + else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true)) { nav_scoring_rect_offset_y = +page_offset_y; g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item) From b30d33378d36546fedc021177129b5882062ff76 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 26 Aug 2020 12:41:05 +0200 Subject: [PATCH 06/13] Nav: Activate InputSource as Gamepad when pressing any of the digital d-pad button. --- imgui.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 9baba2e9..5918e087 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8528,9 +8528,12 @@ static void ImGui::NavUpdate() // (do it before we map Keyboard input!) bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; - if (nav_gamepad_active) - if (io.NavInputs[ImGuiNavInput_Activate] > 0.0f || io.NavInputs[ImGuiNavInput_Input] > 0.0f || io.NavInputs[ImGuiNavInput_Cancel] > 0.0f || io.NavInputs[ImGuiNavInput_Menu] > 0.0f) + if (nav_gamepad_active && g.NavInputSource != ImGuiInputSource_NavGamepad) + { + if (io.NavInputs[ImGuiNavInput_Activate] > 0.0f || io.NavInputs[ImGuiNavInput_Input] > 0.0f || io.NavInputs[ImGuiNavInput_Cancel] > 0.0f || io.NavInputs[ImGuiNavInput_Menu] > 0.0f + || io.NavInputs[ImGuiNavInput_DpadLeft] > 0.0f || io.NavInputs[ImGuiNavInput_DpadRight] > 0.0f || io.NavInputs[ImGuiNavInput_DpadUp] > 0.0f || io.NavInputs[ImGuiNavInput_DpadDown] > 0.0f) g.NavInputSource = ImGuiInputSource_NavGamepad; + } // Update Keyboard->Nav inputs mapping if (nav_keyboard_active) From 8c80d533d96a9b9ef83b1d16dd8396f61f4dd743 Mon Sep 17 00:00:00 2001 From: Louis Schnellbach Date: Wed, 26 Aug 2020 12:18:02 +0200 Subject: [PATCH 07/13] Tab Bar: Fixed a small bug where toggling a tab bar from Reorderable to not Reorderable would leave tabs reordered in the tab list popup. --- docs/CHANGELOG.txt | 2 ++ imgui_internal.h | 6 ++++-- imgui_widgets.cpp | 14 ++++++++------ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 61600a6c..d5d601f9 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -53,6 +53,8 @@ Other Changes: and amends the change done in 1.76 which only affected cases were _OpenOnArrow flag was set. (This is also necessary to support full multi/range-select/drag and drop operations.) - Tab Bar: Keep tab item close button visible while dragging a tab (independent of hovering state). +- Tab Bar: Fixed a small bug where toggling a tab bar from Reorderable to not Reorderable would leave + tabs reordered in the tab list popup. [@Xipiryon] - Metrics: Various tweaks, listing windows front-to-back, greying inactive items when possible. - Demo: Add simple InputText() callbacks demo (aside from the more elaborate ones in 'Examples->Console'). - Examples: Vulkan: Reworked buffer resize handling, fix for Linux/X11. (#3390, #2626) [@RoryO] diff --git a/imgui_internal.h b/imgui_internal.h index 6bf49337..00baf037 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1710,9 +1710,10 @@ struct ImGuiTabItem float Width; // Width currently displayed float ContentWidth; // Width of actual contents, stored during BeginTabItem() call ImS16 NameOffset; // When Window==NULL, offset to name within parent ImGuiTabBar::TabsNames + ImS8 BeginOrder; // BeginTabItem() order, used to re-order tabs after toggling ImGuiTabBarFlags_Reorderable bool WantClose; // Marked as closed by SetTabItemClosed() - ImGuiTabItem() { ID = 0; Flags = ImGuiTabItemFlags_None; LastFrameVisible = LastFrameSelected = -1; NameOffset = -1; Offset = Width = ContentWidth = 0.0f; WantClose = false; } + ImGuiTabItem() { ID = 0; Flags = ImGuiTabItemFlags_None; LastFrameVisible = LastFrameSelected = -1; NameOffset = -1; Offset = Width = ContentWidth = 0.0f; BeginOrder = -1; WantClose = false; } }; // Storage for a tab bar (sizeof() 92~96 bytes) @@ -1737,9 +1738,10 @@ struct ImGuiTabBar ImGuiTabBarFlags Flags; ImGuiID ReorderRequestTabId; ImS8 ReorderRequestDir; + ImS8 TabsActiveCount; // Number of tabs submitted this frame. bool WantLayout; bool VisibleTabWasSubmitted; - short LastTabItemIdx; // For BeginTabItem()/EndTabItem() + short LastTabItemIdx; // Index of last BeginTabItem() tab for use by EndTabItem() ImVec2 FramePadding; // style.FramePadding locked at the time of BeginTabBar() ImGuiTextBuffer TabsNames; // For non-docking tab bar we re-append names in a contiguous buffer. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 59fa0918..352b687a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6782,15 +6782,16 @@ ImGuiTabBar::ImGuiTabBar() Flags = ImGuiTabBarFlags_None; ReorderRequestTabId = 0; ReorderRequestDir = 0; + TabsActiveCount = 0; WantLayout = VisibleTabWasSubmitted = false; LastTabItemIdx = -1; } -static int IMGUI_CDECL TabItemComparerByVisibleOffset(const void* lhs, const void* rhs) +static int IMGUI_CDECL TabItemComparerByBeginOrder(const void* lhs, const void* rhs) { const ImGuiTabItem* a = (const ImGuiTabItem*)lhs; const ImGuiTabItem* b = (const ImGuiTabItem*)rhs; - return (int)(a->Offset - b->Offset); + return (int)(a->BeginOrder - b->BeginOrder); } static ImGuiTabBar* GetTabBarFromTabBarRef(const ImGuiPtrOrIndex& ref) @@ -6842,10 +6843,9 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG return true; } - // When toggling back from ordered to manually-reorderable, shuffle tabs to enforce the last visible order. - // Otherwise, the most recently inserted tabs would move at the end of visible list which can be a little too confusing or magic for the user. - if ((flags & ImGuiTabBarFlags_Reorderable) && !(tab_bar->Flags & ImGuiTabBarFlags_Reorderable) && tab_bar->Tabs.Size > 1 && tab_bar->PrevFrameVisible != -1) - ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByVisibleOffset); + // When toggling ImGuiTabBarFlags_Reorderable flag, ensure tabs are ordered based on their submission order. + if ((flags & ImGuiTabBarFlags_Reorderable) != (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) && tab_bar->Tabs.Size > 1) + ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder); // Flags if ((flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0) @@ -6857,6 +6857,7 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG tab_bar->PrevFrameVisible = tab_bar->CurrFrameVisible; tab_bar->CurrFrameVisible = g.FrameCount; tab_bar->FramePadding = g.Style.FramePadding; + tab_bar->TabsActiveCount = 0; // Set cursor pos in a way which only be used in the off-chance the user erroneously submits item before BeginTabItem(): items will overlap window->DC.CursorPos.x = tab_bar->BarRect.Min.x; @@ -7362,6 +7363,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, } tab_bar->LastTabItemIdx = (short)tab_bar->Tabs.index_from_ptr(tab); tab->ContentWidth = size.x; + tab->BeginOrder = tab_bar->TabsActiveCount++; const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount); const bool tab_bar_focused = (tab_bar->Flags & ImGuiTabBarFlags_IsFocused) != 0; From 45499b8f2f9e95f6e4b4adfe0d29219761ae9378 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 26 Aug 2020 20:18:54 +0200 Subject: [PATCH 08/13] Window: Fixed using non-zero pivot in SetNextWindowPos() when the window is collapsed. (#3433) --- docs/CHANGELOG.txt | 3 ++- imgui.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d5d601f9..6543dacf 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -37,6 +37,8 @@ HOW TO UPDATE? Other Changes: +- Window: Fixed using non-zero pivot in SetNextWindowPos() when the window is collapsed. (#3433) +- Nav: Fixed navigation resuming on first visible item when using gamepad. [@rokups] - InputText: Added selection helpers in ImGuiInputTextCallbackData(). - InputText: Added ImGuiInputTextFlags_CallbackEdit to modify internally owned buffer after an edit. (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the @@ -45,7 +47,6 @@ Other Changes: It is a rather unusual or useless combination of features but no reason it shouldn't work! - DragFloat, DragScalar: Fixed ImGuiSliderFlags_ClampOnInput not being honored in the special case where v_min == v_max. (#3361) -- Nav: Fix navigation resuming on first visible item when using gamepad. [@rokups] - BeginMenuBar: Fixed minor bug where CursorPosMax gets pushed to CursorPos prior to calling BeginMenuBar(), so e.g. calling the function at the end of a window would often add +ItemSpacing.y to scrolling range. - TreeNode, CollapsingHeader: Made clicking on arrow toggle toggle the open state on the Mouse Down event diff --git a/imgui.cpp b/imgui.cpp index 5918e087..922cbd0e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5721,7 +5721,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0); if (window_pos_with_pivot) - SetWindowPos(window, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering) + SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering) else if ((flags & ImGuiWindowFlags_ChildMenu) != 0) window->Pos = FindBestWindowPosForPopup(window); else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize) From 093afd4f7f1ae3b0cb204896b134e01a142829e1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 26 Aug 2020 20:50:19 +0200 Subject: [PATCH 09/13] Internals: Added Name to ImGuiDataTypeInfo + minor misc comments in BeginGroup(). --- imgui.cpp | 11 ++++++----- imgui_internal.h | 3 ++- imgui_widgets.cpp | 24 ++++++++++++------------ 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 922cbd0e..c352c8a5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7271,10 +7271,11 @@ float ImGui::GetWindowContentRegionWidth() } // Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) +// Groups are currently a mishmash of functionalities which should perhaps be clarified and separated. void ImGui::BeginGroup() { ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); + ImGuiWindow* window = g.CurrentWindow; window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1); ImGuiGroupData& group_data = window->DC.GroupStack.back(); @@ -7299,8 +7300,8 @@ void ImGui::BeginGroup() void ImGui::EndGroup() { ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls + ImGuiWindow* window = g.CurrentWindow; + IM_ASSERT(window->DC.GroupStack.Size > 0); // Mismatched BeginGroup()/EndGroup() calls ImGuiGroupData& group_data = window->DC.GroupStack.back(); @@ -7328,9 +7329,9 @@ void ImGui::EndGroup() // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive(), IsItemDeactivated() etc. will be functional on the entire group. // It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but would put a little more burden on individual widgets. // Also if you grep for LastItemId you'll notice it is only used in that context. - // (The tests not symmetrical because ActiveIdIsAlive is an ID itself, in order to be able to handle ActiveId being overwritten during the frame.) + // (The two tests not the same because ActiveIdIsAlive is an ID itself, in order to be able to handle ActiveId being overwritten during the frame.) const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId; - const bool group_contains_prev_active_id = !group_data.BackupActiveIdPreviousFrameIsAlive && g.ActiveIdPreviousFrameIsAlive; + const bool group_contains_prev_active_id = (group_data.BackupActiveIdPreviousFrameIsAlive == false) && (g.ActiveIdPreviousFrameIsAlive == true); if (group_contains_curr_active_id) window->DC.LastItemId = g.ActiveId; else if (group_contains_prev_active_id) diff --git a/imgui_internal.h b/imgui_internal.h index 00baf037..5f55d218 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -786,7 +786,8 @@ struct ImGuiDataTypeTempStorage // Type information associated to one ImGuiDataType. Retrieve with DataTypeGetInfo(). struct ImGuiDataTypeInfo { - size_t Size; // Size in byte + size_t Size; // Size in bytes + const char* Name; // Short descriptive name for the type, for debugging const char* PrintFmt; // Default printf format for the type const char* ScanFmt; // Default scanf format for the type }; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 352b687a..691b36f7 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1687,21 +1687,21 @@ bool ImGui::Combo(const char* label, int* current_item, const char* items_separa static const ImGuiDataTypeInfo GDataTypeInfo[] = { - { sizeof(char), "%d", "%d" }, // ImGuiDataType_S8 - { sizeof(unsigned char), "%u", "%u" }, - { sizeof(short), "%d", "%d" }, // ImGuiDataType_S16 - { sizeof(unsigned short), "%u", "%u" }, - { sizeof(int), "%d", "%d" }, // ImGuiDataType_S32 - { sizeof(unsigned int), "%u", "%u" }, + { sizeof(char), "S8", "%d", "%d" }, // ImGuiDataType_S8 + { sizeof(unsigned char), "U8", "%u", "%u" }, + { sizeof(short), "S16", "%d", "%d" }, // ImGuiDataType_S16 + { sizeof(unsigned short), "U16", "%u", "%u" }, + { sizeof(int), "S32", "%d", "%d" }, // ImGuiDataType_S32 + { sizeof(unsigned int), "U32", "%u", "%u" }, #ifdef _MSC_VER - { sizeof(ImS64), "%I64d","%I64d" }, // ImGuiDataType_S64 - { sizeof(ImU64), "%I64u","%I64u" }, + { sizeof(ImS64), "S64", "%I64d","%I64d" }, // ImGuiDataType_S64 + { sizeof(ImU64), "U64", "%I64u","%I64u" }, #else - { sizeof(ImS64), "%lld", "%lld" }, // ImGuiDataType_S64 - { sizeof(ImU64), "%llu", "%llu" }, + { sizeof(ImS64), "S64", "%lld", "%lld" }, // ImGuiDataType_S64 + { sizeof(ImU64), "U64", "%llu", "%llu" }, #endif - { sizeof(float), "%f", "%f" }, // ImGuiDataType_Float (float are promoted to double in va_arg) - { sizeof(double), "%f", "%lf" }, // ImGuiDataType_Double + { sizeof(float), "float", "%f", "%f" }, // ImGuiDataType_Float (float are promoted to double in va_arg) + { sizeof(double), "double","%f", "%lf" }, // ImGuiDataType_Double }; IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT); From 302896d4884896128ffe59257bb64b4f5af8aae5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 27 Aug 2020 12:19:13 +0200 Subject: [PATCH 10/13] Basic optimization for ShadeVertsLinearColorGradientKeepAlpha() - especially for debug overhead - since it's used massively by some of our experiments. --- imgui_draw.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index bb300e00..5b79a449 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1599,13 +1599,19 @@ void ImGui::ShadeVertsLinearColorGradientKeepAlpha(ImDrawList* draw_list, int ve float gradient_inv_length2 = 1.0f / ImLengthSqr(gradient_extent); ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx; ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx; + const int col0_r = (int)(col0 >> IM_COL32_R_SHIFT) & 0xFF; + const int col0_g = (int)(col0 >> IM_COL32_G_SHIFT) & 0xFF; + const int col0_b = (int)(col0 >> IM_COL32_B_SHIFT) & 0xFF; + const int col_delta_r = ((int)(col1 >> IM_COL32_R_SHIFT) & 0xFF) - col0_r; + const int col_delta_g = ((int)(col1 >> IM_COL32_G_SHIFT) & 0xFF) - col0_g; + const int col_delta_b = ((int)(col1 >> IM_COL32_B_SHIFT) & 0xFF) - col0_b; for (ImDrawVert* vert = vert_start; vert < vert_end; vert++) { float d = ImDot(vert->pos - gradient_p0, gradient_extent); float t = ImClamp(d * gradient_inv_length2, 0.0f, 1.0f); - int r = ImLerp((int)(col0 >> IM_COL32_R_SHIFT) & 0xFF, (int)(col1 >> IM_COL32_R_SHIFT) & 0xFF, t); - int g = ImLerp((int)(col0 >> IM_COL32_G_SHIFT) & 0xFF, (int)(col1 >> IM_COL32_G_SHIFT) & 0xFF, t); - int b = ImLerp((int)(col0 >> IM_COL32_B_SHIFT) & 0xFF, (int)(col1 >> IM_COL32_B_SHIFT) & 0xFF, t); + int r = (int)(col0_r + col_delta_r * t); + int g = (int)(col0_g + col_delta_g * t); + int b = (int)(col0_b + col_delta_b * t); vert->col = (r << IM_COL32_R_SHIFT) | (g << IM_COL32_G_SHIFT) | (b << IM_COL32_B_SHIFT) | (vert->col & IM_COL32_A_MASK); } } From 901d432cb7b2fa25bac2ccf8f55805e65aa9debc Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 27 Aug 2020 19:51:25 +0200 Subject: [PATCH 11/13] Nav: Fixed using Alt to toggle the Menu layer when inside a Modal window. (#787) Tidying up todo items. --- docs/CHANGELOG.txt | 1 + docs/TODO.txt | 18 +++++++++--------- imgui.cpp | 10 ++++------ 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 6543dacf..70235720 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -39,6 +39,7 @@ Other Changes: - Window: Fixed using non-zero pivot in SetNextWindowPos() when the window is collapsed. (#3433) - Nav: Fixed navigation resuming on first visible item when using gamepad. [@rokups] +- Nav: Fixed using Alt to toggle the Menu layer when inside a Modal window. (#787) - InputText: Added selection helpers in ImGuiInputTextCallbackData(). - InputText: Added ImGuiInputTextFlags_CallbackEdit to modify internally owned buffer after an edit. (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the diff --git a/docs/TODO.txt b/docs/TODO.txt index e1b1f3e3..509fcca5 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -333,20 +333,24 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - font/opt: Glyph currently 40 bytes (2+9*4). Consider storing UV as 16 bits integer? (->32 bytes). X0/Y0/X1/Y1 as 16 fixed-point integers? Or X0/Y0 as float and X1/Y1 as fixed8_8? - nav: some features such as PageUp/Down/Home/End should probably work without ImGuiConfigFlags_NavEnableKeyboard? (where do we draw the line?) - ! nav: never clear NavId on some setup (e.g. gamepad centric) - - nav: code to focus child-window on restoring NavId appears to have issue: e.g. when focus change is implicit because of window closure. - nav: configuration flag to disable global shortcuts (currently only CTRL-Tab) ? + ! nav: never clear NavId on some setup (e.g. gamepad centric) + - nav: there's currently no way to completely clear focus with the keyboard. depending on patterns used by the application to dispatch inputs, it may be desirable. + - nav: code to focus child-window on restoring NavId appears to have issue: e.g. when focus change is implicit because of window closure. - nav: Home/End behavior when navigable item is not fully visible at the edge of scrolling? should be backtrack to keep item into view? - nav: NavScrollToBringItemIntoView() with item bigger than view should focus top-right? Repro: using Nav in "About Window" - nav: wrap around logic to allow e.g. grid based layout (pressing NavRight on the right-most element would go to the next row, etc.). see internal's NavMoveRequestTryWrapping(). - - nav: patterns to make it possible for arrows key to update selection + - nav: patterns to make it possible for arrows key to update selection (see JustMovedTo in range_select branch) - nav: restore/find nearest NavId when current one disappear (e.g. pressed a button that disappear, or perhaps auto restoring when current button change name) - nav: SetItemDefaultFocus() level of priority, so widget like Selectable when inside a popup could claim a low-priority default focus on the first selected iem - nav: NavFlattened: ESC on a flattened child should select something. - nav: NavFlattened: broken: in typical usage scenario, the items of a fully clipped child are currently not considered to enter into a NavFlattened child. - nav: NavFlattened: init request doesn't select items that are part of a NavFlattened child - nav: NavFlattened: cannot access menu-bar of a flattened child window with Alt/menu key (not a very common use case..). - - nav/treenode: Left within a tree node block as a fallback (ImGuiTreeNodeFlags_NavLeftJumpsBackHere by default?) + - nav: simulate right-click or context activation? (SHIFT+F10) + - nav/tabbing: refactor old tabbing system and turn into navigation, should pass through all widgets (in submission order?). + - nav/popup: esc/enter default behavior for popups, e.g. be able to mark an "ok" or "cancel" button that would get triggered by those keys, default validation button, etc. + - nav/treenode: left within a tree node block as a fallback (ImGuiTreeNodeFlags_NavLeftJumpsBackHere by default?) - nav/menus: pressing left-right on a vertically clipped menu bar tends to jump to the collapse/close buttons. - nav/menus: allow pressing Menu to leave a sub-menu. - nav/menus: a way to access the main menu bar with Alt? (currently needs CTRL+TAB) or last focused window menu bar? @@ -355,11 +359,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - nav/menus: Alt,Up could open the first menu (e.g. "File") currently it tends to nav into the window/collapse menu. Do do that we would need custom transition? - nav/windowing: configure fade-in/fade-out delay on Ctrl+Tab? - nav/windowing: when CTRL-Tab/windowing is active, the HoveredWindow detection doesn't take account of the window display re-ordering. - - nav: simulate right-click or context activation? (SHIFT+F10) - - nav: tabs should go through most/all widgets (in submission order?). - - nav: esc/enter default behavior for popups, e.g. be able to mark an "ok" or "cancel" button that would get triggered by those keys. - - nav: when activating a button that changes label (without a static ID) or disappear, can we somehow automatically recover into a nearest highlight item? - - nav: there's currently no way to completely clear focus with the keyboard. depending on patterns used by the application to dispatch inputs, it may be desirable. + - nav/windowing: Resizing window will currently fail with certain types of resizing constraints/callback applied - focus: preserve ActiveId/focus stack state, e.g. when opening a menu and close it, previously selected InputText() focus gets restored (#622) - focus: SetKeyboardFocusHere() on with >= 0 offset could be done on same frame (else latch and modulate on beginning of next frame) - focus: unable to use SetKeyboardFocusHere() on clipped widgets. (#787) diff --git a/imgui.cpp b/imgui.cpp index c352c8a5..36c4df2d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9039,11 +9039,9 @@ static void ImGui::NavUpdateWindowing() bool apply_toggle_layer = false; ImGuiWindow* modal_window = GetTopMostPopupModal(); - if (modal_window != NULL) - { + bool allow_windowing = (modal_window == NULL); + if (!allow_windowing) g.NavWindowingTarget = NULL; - return; - } // Fade out if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL) @@ -9054,8 +9052,8 @@ static void ImGui::NavUpdateWindowing() } // Start CTRL-TAB or Square+L/R window selection - bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); - bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard); + bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); + bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard); if (start_windowing_with_gamepad || start_windowing_with_keyboard) if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) { From 13f718337ac4df2c66727e85174d48e9a91eabcf Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 28 Aug 2020 16:49:46 +0200 Subject: [PATCH 12/13] Internals: Added support for overriding locale decimal point, undocumented. (#2278) + Misc doc update. Doc: Mention IMGUI_VERSION_NUM in recent api breaking changes + textwrap some demo code. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 1 + imgui.h | 3 ++- imgui_demo.cpp | 46 +++++++++++++++++++++++++++++++++------------- imgui_internal.h | 2 ++ imgui_widgets.cpp | 12 ++++++++++-- 6 files changed, 49 insertions(+), 16 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 70235720..032360ad 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -88,6 +88,7 @@ Breaking Changes: - You can replace the >1.0f value with ImGuiSliderFlags_Logarithmic to fix the warning/assert and get a _similar_ effect as previous uses of power >1.0f. See https://github.com/ocornut/imgui/issues/3361 for all details. + For shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`. Kept inline redirection functions (will obsolete) apart for: DragFloatRange2(), VSliderFloat(), VSliderScalar(). For those three the 'float power=1.0f' version was removed directly as they were most unlikely ever used. - DragInt, DragFloat, DragScalar: Obsoleted use of v_min > v_max to lock edits (introduced in 1.73, this was not diff --git a/imgui.cpp b/imgui.cpp index 36c4df2d..9fe70421 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -380,6 +380,7 @@ CODE - if you set the 'power' parameter to >1.0f (to enable non-linear editing): 1/ your compiler may warn on float>int conversion, 2/ code will assert at runtime, 3/ in case asserts are disabled, the code will not crash and enable the _Logarithmic flag. 4/ you can replace the >1.0f value with ImGuiSliderFlags_Logarithmic to fix the warning/assert and get a _similar_ effect as previous uses of power >1.0f. see https://github.com/ocornut/imgui/issues/3361 for all details. kept inline redirection functions (will obsolete) apart for: DragFloatRange2(), VSliderFloat(), VSliderScalar(). For those three the 'float power=1.0f' version were removed directly as they were most unlikely ever used. + for shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`. - obsoleted use of v_min > v_max in DragInt, DragFloat, DragScalar to lock edits (introduced in 1.73, was not demoed nor documented very), will be replaced by a more generic ReadOnly feature. You may use the ImGuiSliderFlags_ReadOnly internal flag in the meantime. - 2020/06/23 (1.77) - removed BeginPopupContextWindow(const char*, int mouse_button, bool also_over_items) in favor of BeginPopupContextWindow(const char*, ImGuiPopupFlags flags) with ImGuiPopupFlags_NoOverItems. - 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete). diff --git a/imgui.h b/imgui.h index 27ef591d..40ffedad 100644 --- a/imgui.h +++ b/imgui.h @@ -1712,7 +1712,8 @@ struct ImGuiPayload namespace ImGui { // OBSOLETED in 1.78 (from August 2020) - // Old drag/sliders functions that took a 'float power = 1.0' argument instead of flags + // Old drag/sliders functions that took a 'float power = 1.0' argument instead of flags. + // For shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`. IMGUI_API bool DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, const char* format, float power); IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min, const void* p_max, const char* format, float power); static inline bool DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, float power) { return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, power); } diff --git a/imgui_demo.cpp b/imgui_demo.cpp index efb83a4d..05bb729a 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -560,7 +560,9 @@ static void ShowDemoWindowWidgets() const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIIIIII", "JJJJ", "KKKKKKK" }; static int item_current = 0; ImGui::Combo("combo", &item_current, items, IM_ARRAYSIZE(items)); - ImGui::SameLine(); HelpMarker("Refer to the \"Combo\" section below for an explanation of the full BeginCombo/EndCombo API, and demonstration of various flags.\n"); + ImGui::SameLine(); HelpMarker( + "Refer to the \"Combo\" section below for an explanation of the full BeginCombo/EndCombo API, " + "and demonstration of various flags.\n"); } { @@ -834,7 +836,9 @@ static void ShowDemoWindowWidgets() if (ImGui::TreeNode("Word Wrapping")) { // Using shortcut. You can use PushTextWrapPos()/PopTextWrapPos() for more flexibility. - ImGui::TextWrapped("This text should automatically wrap on the edge of the window. The current implementation for text wrapping follows simple rules suitable for English and possibly other languages."); + ImGui::TextWrapped( + "This text should automatically wrap on the edge of the window. The current implementation " + "for text wrapping follows simple rules suitable for English and possibly other languages."); ImGui::Spacing(); static float wrap_width = 200.0f; @@ -891,7 +895,10 @@ static void ShowDemoWindowWidgets() if (ImGui::TreeNode("Images")) { ImGuiIO& io = ImGui::GetIO(); - ImGui::TextWrapped("Below we are displaying the font texture (which is the only texture we have access to in this demo). Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture data. Hover the texture for a zoomed view!"); + ImGui::TextWrapped( + "Below we are displaying the font texture (which is the only texture we have access to in this demo). " + "Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture data. " + "Hover the texture for a zoomed view!"); // Below we are displaying the font texture because it is the only texture we have access to inside the demo! // Remember that ImTextureID is just storage for whatever you want it to be. It is essentially a value that @@ -1000,9 +1007,9 @@ static void ShowDemoWindowWidgets() ImGui::Combo("combo 3 (array)", &item_current_3, items, IM_ARRAYSIZE(items)); // Simplified one-liner Combo() using an accessor function - struct FuncHolder { static bool ItemGetter(void* data, int idx, const char** out_str) { *out_str = ((const char**)data)[idx]; return true; } }; + struct Funcs { static bool ItemGetter(void* data, int n, const char** out_str) { *out_str = ((const char**)data)[n]; return true; } }; static int item_current_4 = 0; - ImGui::Combo("combo 4 (function)", &item_current_4, &FuncHolder::ItemGetter, items, IM_ARRAYSIZE(items)); + ImGui::Combo("combo 4 (function)", &item_current_4, &Funcs::ItemGetter, items, IM_ARRAYSIZE(items)); ImGui::TreePop(); } @@ -1640,7 +1647,10 @@ static void ShowDemoWindowWidgets() const float drag_speed = 0.2f; static bool drag_clamp = false; ImGui::Text("Drags:"); - ImGui::Checkbox("Clamp integers to 0..50", &drag_clamp); ImGui::SameLine(); HelpMarker("As with every widgets in dear imgui, we never modify values unless there is a user interaction.\nYou can override the clamping limits by using CTRL+Click to input a value."); + ImGui::Checkbox("Clamp integers to 0..50", &drag_clamp); + ImGui::SameLine(); HelpMarker( + "As with every widgets in dear imgui, we never modify values unless there is a user interaction.\n" + "You can override the clamping limits by using CTRL+Click to input a value."); ImGui::DragScalar("drag s8", ImGuiDataType_S8, &s8_v, drag_speed, drag_clamp ? &s8_zero : NULL, drag_clamp ? &s8_fifty : NULL); ImGui::DragScalar("drag u8", ImGuiDataType_U8, &u8_v, drag_speed, drag_clamp ? &u8_zero : NULL, drag_clamp ? &u8_fifty : NULL, "%u ms"); ImGui::DragScalar("drag s16", ImGuiDataType_S16, &s16_v, drag_speed, drag_clamp ? &s16_zero : NULL, drag_clamp ? &s16_fifty : NULL); @@ -3957,9 +3967,13 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) if (ImGui::BeginTabItem("Rendering")) { ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines); - ImGui::SameLine(); HelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well."); + ImGui::SameLine(); + HelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well."); + ImGui::Checkbox("Anti-aliased lines use texture", &style.AntiAliasedLinesUseTex); - ImGui::SameLine(); HelpMarker("Faster lines using texture data. Require back-end to render with bilinear filtering (not point/nearest filtering)."); + ImGui::SameLine(); + HelpMarker("Faster lines using texture data. Require back-end to render with bilinear filtering (not point/nearest filtering)."); + ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill); ImGui::PushItemWidth(100); ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, "%.2f"); @@ -3972,12 +3986,13 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SetNextWindowPos(ImGui::GetCursorScreenPos()); ImGui::BeginTooltip(); ImVec2 p = ImGui::GetCursorScreenPos(); + ImDrawList* draw_list = ImGui::GetWindowDrawList(); float RAD_MIN = 10.0f, RAD_MAX = 80.0f; float off_x = 10.0f; for (int n = 0; n < 7; n++) { const float rad = RAD_MIN + (RAD_MAX - RAD_MIN) * (float)n / (7.0f - 1.0f); - ImGui::GetWindowDrawList()->AddCircle(ImVec2(p.x + off_x + rad, p.y + RAD_MAX), rad, ImGui::GetColorU32(ImGuiCol_Text), 0); + draw_list->AddCircle(ImVec2(p.x + off_x + rad, p.y + RAD_MAX), rad, ImGui::GetColorU32(ImGuiCol_Text), 0); off_x += 10.0f + rad * 2.0f; } ImGui::Dummy(ImVec2(off_x, RAD_MAX * 2.0f)); @@ -4200,9 +4215,12 @@ struct ExampleAppConsole // TODO: display items starting from the bottom - if (ImGui::SmallButton("Add Debug Text")) { AddLog("%d some text", Items.Size); AddLog("some more text"); AddLog("display very important message here!"); } ImGui::SameLine(); - if (ImGui::SmallButton("Add Debug Error")) { AddLog("[error] something went wrong"); } ImGui::SameLine(); - if (ImGui::SmallButton("Clear")) { ClearLog(); } ImGui::SameLine(); + if (ImGui::SmallButton("Add Debug Text")) { AddLog("%d some text", Items.Size); AddLog("some more text"); AddLog("display very important message here!"); } + ImGui::SameLine(); + if (ImGui::SmallButton("Add Debug Error")) { AddLog("[error] something went wrong"); } + ImGui::SameLine(); + if (ImGui::SmallButton("Clear")) { ClearLog(); } + ImGui::SameLine(); bool copy_to_clipboard = ImGui::SmallButton("Copy"); //static float t = 0.0f; if (ImGui::GetTime() - t > 0.02f) { t = ImGui::GetTime(); AddLog("Spam %f", t); } @@ -4695,7 +4713,9 @@ static void ShowPlaceholderObject(const char* prefix, int uid) { // Use object uid as identifier. Most commonly you could also use the object pointer as a base ID. ImGui::PushID(uid); - ImGui::AlignTextToFramePadding(); // Text and Tree nodes are less high than framed widgets, here we add vertical spacing to make the tree lines equal high. + + // Text and Tree nodes are less high than framed widgets, using AlignTextToFramePadding() we add vertical spacing to make the tree lines equal high. + ImGui::AlignTextToFramePadding(); bool node_open = ImGui::TreeNode("Object", "%s_%u", prefix, uid); ImGui::NextColumn(); ImGui::AlignTextToFramePadding(); diff --git a/imgui_internal.h b/imgui_internal.h index 5f55d218..6c9a3f75 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1290,6 +1290,7 @@ struct ImGuiContext // Platform support ImVec2 PlatformImePos; // Cursor position request & last passed to the OS Input Method Editor ImVec2 PlatformImeLastPos; + char PlatformLocaleDecimalPoint; // '.' or *localeconv()->decimal_point // Settings bool SettingsLoaded; @@ -1440,6 +1441,7 @@ struct ImGuiContext TooltipOverrideCount = 0; PlatformImePos = PlatformImeLastPos = ImVec2(FLT_MAX, FLT_MAX); + PlatformLocaleDecimalPoint = '.'; SettingsLoaded = false; SettingsDirtyTimer = 0.0f; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 691b36f7..181fbd7b 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3691,14 +3691,22 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f // Generic named filters if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific)) { + // The libc allows overriding locale, with e.g. 'setlocale(LC_NUMERIC, "de_DE.UTF-8");' which affect the output/input of printf/scanf. + // The standard mandate that programs starts in the "C" locale where the decimal point is '.'. + // We don't really intend to provide widespread support for it, but out of empathy for people stuck with using odd API, we support the bare minimum aka overriding the decimal point. + // Change the default decimal_point with: + // ImGui::GetCurrentContext()->PlatformLocaleDecimalPoint = *localeconv()->decimal_point; + ImGuiContext& g = *GImGui; + const unsigned c_decimal_point = (unsigned int)g.PlatformLocaleDecimalPoint; + // Allow 0-9 . - + * / if (flags & ImGuiInputTextFlags_CharsDecimal) - if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/')) + if (!(c >= '0' && c <= '9') && (c != c_decimal_point) && (c != '-') && (c != '+') && (c != '*') && (c != '/')) return false; // Allow 0-9 . - + * / e E if (flags & ImGuiInputTextFlags_CharsScientific) - if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/') && (c != 'e') && (c != 'E')) + if (!(c >= '0' && c <= '9') && (c != c_decimal_point) && (c != '-') && (c != '+') && (c != '*') && (c != '/') && (c != 'e') && (c != 'E')) return false; // Allow 0-9 a-F A-F From ce230fc370c29af565b3e380a521b07a7f321563 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 31 Aug 2020 17:25:16 +0200 Subject: [PATCH 13/13] Internals: TabBar renaming and shuffling stuff around. + sneaking a readme change --- docs/README.md | 2 +- imgui.cpp | 4 +-- imgui_internal.h | 7 ++--- imgui_widgets.cpp | 66 ++++++++++++++++++++++++++--------------------- 4 files changed, 44 insertions(+), 35 deletions(-) diff --git a/docs/README.md b/docs/README.md index 58fb208e..230aa03c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -115,7 +115,7 @@ Officially maintained bindings (in repository): Third-party bindings (see [Bindings](https://github.com/ocornut/imgui/wiki/Bindings/) page): - Languages: C, C# and: Beef, ChaiScript, D, Go, Haskell, Haxe/hxcpp, Java, JavaScript, Julia, Kotlin, Lua, Odin, Pascal, PureBasic, Python, Ruby, Rust, Swift... -- Frameworks: AGS/Adventure Game Studio, Amethyst, bsf, Cinder, Cocos2d-x, Diligent Engine, Flexium, GML/Game Maker Studio2, Godot, GTK3+OpenGL3, Irrlicht Engine, LÖVE+LUA, Magnum, NanoRT, Nim Game Lib, Ogre, openFrameworks, OSG/OpenSceneGraph, Orx, Photoshop, px_render, Qt/QtDirect3D, SFML, Sokol, Unity, Unreal Engine 4, vtk, Win32 GDI, WxWidgets. +- Frameworks: AGS/Adventure Game Studio, Amethyst, bsf, Cinder, Cocos2d-x, Diligent Engine, Flexium, GML/Game Maker Studio2, Godot, GTK3+OpenGL3, Irrlicht Engine, LÖVE+LUA, Magnum, NanoRT, nCine, Nim Game Lib, Ogre, openFrameworks, OSG/OpenSceneGraph, Orx, Photoshop, px_render, Qt/QtDirect3D, SFML, Sokol, Unity, Unreal Engine 4, vtk, Win32 GDI, WxWidgets. - Note that C bindings ([cimgui](https://github.com/cimgui/cimgui)) are auto-generated, you can use its json/lua output to generate bindings for other languages. Also see [Wiki](https://github.com/ocornut/imgui/wiki) for more links and ideas. diff --git a/imgui.cpp b/imgui.cpp index 9fe70421..f0d2a47a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10504,8 +10504,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) { const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; ImGui::PushID(tab); - if (ImGui::SmallButton("<")) { TabBarQueueChangeTabOrder(tab_bar, tab, -1); } ImGui::SameLine(0, 2); - if (ImGui::SmallButton(">")) { TabBarQueueChangeTabOrder(tab_bar, tab, +1); } ImGui::SameLine(); + if (ImGui::SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } ImGui::SameLine(0, 2); + if (ImGui::SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } ImGui::SameLine(); ImGui::Text("%02d%c Tab 0x%08X '%s'", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : ""); ImGui::PopID(); } diff --git a/imgui_internal.h b/imgui_internal.h index 6c9a3f75..7c5cb0fd 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1731,8 +1731,8 @@ struct ImGuiTabBar int PrevFrameVisible; ImRect BarRect; float LastTabContentHeight; // Record the height of contents submitted below the tab bar - float OffsetMax; // Distance from BarRect.Min.x, locked during layout - float OffsetMaxIdeal; // Ideal offset if all tabs were visible and not clipped + float WidthAllTabs; // Actual width of all tabs (locked during layout) + float WidthAllTabsIdeal; // Ideal width if all tabs were visible and not clipped float OffsetNextTab; // Distance from BarRect.Min.x, incremented with each BeginTabItem() call, not used if ImGuiTabBarFlags_Reorderable if set. float ScrollingAnim; float ScrollingTarget; @@ -1931,7 +1931,8 @@ namespace ImGui IMGUI_API ImGuiTabItem* TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id); IMGUI_API void TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id); IMGUI_API void TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); - IMGUI_API void TabBarQueueChangeTabOrder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir); + IMGUI_API void TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir); + IMGUI_API bool TabBarProcessReorder(ImGuiTabBar* tab_bar); IMGUI_API bool TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags); IMGUI_API ImVec2 TabItemCalcSize(const char* label, bool has_close_button); IMGUI_API void TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 181fbd7b..eb5e4ad9 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6761,7 +6761,7 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, // - TabBarFindTabById() [Internal] // - TabBarRemoveTab() [Internal] // - TabBarCloseTab() [Internal] -// - TabBarScrollClamp()v +// - TabBarScrollClamp() [Internal] // - TabBarScrollToTab() [Internal] // - TabBarQueueChangeTabOrder() [Internal] // - TabBarScrollingButtons() [Internal] @@ -6785,7 +6785,7 @@ ImGuiTabBar::ImGuiTabBar() SelectedTabId = NextSelectedTabId = VisibleTabId = 0; CurrFrameVisible = PrevFrameVisible = -1; LastTabContentHeight = 0.0f; - OffsetMax = OffsetMaxIdeal = OffsetNextTab = 0.0f; + WidthAllTabs = WidthAllTabsIdeal = OffsetNextTab = 0.0f; ScrollingAnim = ScrollingTarget = ScrollingTargetDistToVisibility = ScrollingSpeed = 0.0f; Flags = ImGuiTabBarFlags_None; ReorderRequestTabId = 0; @@ -6951,23 +6951,9 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) // Process order change request (we could probably process it when requested but it's just saner to do it in a single spot). if (tab_bar->ReorderRequestTabId != 0) { - if (ImGuiTabItem* tab1 = TabBarFindTabByID(tab_bar, tab_bar->ReorderRequestTabId)) - { - //IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_Reorderable); // <- this may happen when using debug tools - int tab2_order = tab_bar->GetTabOrder(tab1) + tab_bar->ReorderRequestDir; - if (tab2_order >= 0 && tab2_order < tab_bar->Tabs.Size) - { - ImGuiTabItem* tab2 = &tab_bar->Tabs[tab2_order]; - ImGuiTabItem item_tmp = *tab1; - *tab1 = *tab2; - *tab2 = item_tmp; - if (tab2->ID == tab_bar->SelectedTabId) - scroll_track_selected_tab_id = tab2->ID; - tab1 = tab2 = NULL; - } - if (tab_bar->Flags & ImGuiTabBarFlags_SaveSettings) - MarkIniSettingsDirty(); - } + if (TabBarProcessReorder(tab_bar)) + if (tab_bar->ReorderRequestTabId == tab_bar->SelectedTabId) + scroll_track_selected_tab_id = tab_bar->ReorderRequestTabId; tab_bar->ReorderRequestTabId = 0; } @@ -7041,11 +7027,11 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) offset_x += tab->Width + g.Style.ItemInnerSpacing.x; offset_x_ideal += tab->ContentWidth + g.Style.ItemInnerSpacing.x; } - tab_bar->OffsetMax = ImMax(offset_x - g.Style.ItemInnerSpacing.x, 0.0f); - tab_bar->OffsetMaxIdeal = ImMax(offset_x_ideal - g.Style.ItemInnerSpacing.x, 0.0f); + tab_bar->WidthAllTabs = ImMax(offset_x - g.Style.ItemInnerSpacing.x, 0.0f); + tab_bar->WidthAllTabsIdeal = ImMax(offset_x_ideal - g.Style.ItemInnerSpacing.x, 0.0f); // Horizontal scrolling buttons - const bool scrolling_buttons = (tab_bar->OffsetMax > tab_bar->BarRect.GetWidth() && tab_bar->Tabs.Size > 1) && !(tab_bar->Flags & ImGuiTabBarFlags_NoTabListScrollingButtons) && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll); + const bool scrolling_buttons = (tab_bar->WidthAllTabs > tab_bar->BarRect.GetWidth() && tab_bar->Tabs.Size > 1) && !(tab_bar->Flags & ImGuiTabBarFlags_NoTabListScrollingButtons) && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll); if (scrolling_buttons) if (ImGuiTabItem* tab_to_select = TabBarScrollingButtons(tab_bar)) // NB: Will alter BarRect.Max.x! scroll_track_selected_tab_id = tab_bar->SelectedTabId = tab_to_select->ID; @@ -7087,7 +7073,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) // Actual layout in host window (we don't do it in BeginTabBar() so as not to waste an extra frame) ImGuiWindow* window = g.CurrentWindow; window->DC.CursorPos = tab_bar->BarRect.Min; - ItemSize(ImVec2(tab_bar->OffsetMaxIdeal, tab_bar->BarRect.GetHeight()), tab_bar->FramePadding.y); + ItemSize(ImVec2(tab_bar->WidthAllTabsIdeal, tab_bar->BarRect.GetHeight()), tab_bar->FramePadding.y); } // Dockables uses Name/ID in the global namespace. Non-dockable items use the ID stack. @@ -7150,7 +7136,7 @@ void ImGui::TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) static float ImGui::TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling) { - scrolling = ImMin(scrolling, tab_bar->OffsetMax - tab_bar->BarRect.GetWidth()); + scrolling = ImMin(scrolling, tab_bar->WidthAllTabs - tab_bar->BarRect.GetWidth()); return ImMax(scrolling, 0.0f); } @@ -7174,7 +7160,7 @@ static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) } } -void ImGui::TabBarQueueChangeTabOrder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir) +void ImGui::TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir) { IM_ASSERT(dir == -1 || dir == +1); IM_ASSERT(tab_bar->ReorderRequestTabId == 0); @@ -7182,6 +7168,28 @@ void ImGui::TabBarQueueChangeTabOrder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab_bar->ReorderRequestDir = (ImS8)dir; } +bool ImGui::TabBarProcessReorder(ImGuiTabBar* tab_bar) +{ + ImGuiTabItem* tab1 = TabBarFindTabByID(tab_bar, tab_bar->ReorderRequestTabId); + if (!tab1) + return false; + + //IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_Reorderable); // <- this may happen when using debug tools + int tab2_order = tab_bar->GetTabOrder(tab1) + tab_bar->ReorderRequestDir; + if (tab2_order < 0 || tab2_order >= tab_bar->Tabs.Size) + return false; + + ImGuiTabItem* tab2 = &tab_bar->Tabs[tab2_order]; + ImGuiTabItem item_tmp = *tab1; + *tab1 = *tab2; + *tab2 = item_tmp; + tab1 = tab2 = NULL; + + if (tab_bar->Flags & ImGuiTabBarFlags_SaveSettings) + MarkIniSettingsDirty(); + return true; +} + static ImGuiTabItem* ImGui::TabBarScrollingButtons(ImGuiTabBar* tab_bar) { ImGuiContext& g = *GImGui; @@ -7293,7 +7301,7 @@ bool ImGui::BeginTabItem(const char* label, bool* p_open, ImGuiTabItemFlags f ImGuiTabBar* tab_bar = g.CurrentTabBar; if (tab_bar == NULL) { - IM_ASSERT_USER_ERROR(tab_bar, "BeginTabItem() Needs to be called between BeginTabBar() and EndTabBar()!"); + IM_ASSERT_USER_ERROR(tab_bar, "Needs to be called between BeginTabBar() and EndTabBar()!"); return false; } bool ret = TabItemEx(tab_bar, label, p_open, flags); @@ -7315,7 +7323,7 @@ void ImGui::EndTabItem() ImGuiTabBar* tab_bar = g.CurrentTabBar; if (tab_bar == NULL) { - IM_ASSERT(tab_bar != NULL && "Needs to be called between BeginTabBar() and EndTabBar()!"); + IM_ASSERT_USER_ERROR(tab_bar != NULL, "Needs to be called between BeginTabBar() and EndTabBar()!"); return; } IM_ASSERT(tab_bar->LastTabItemIdx >= 0); @@ -7471,12 +7479,12 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < bb.Min.x) { if (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) - TabBarQueueChangeTabOrder(tab_bar, tab, -1); + TabBarQueueReorder(tab_bar, tab, -1); } else if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > bb.Max.x) { if (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) - TabBarQueueChangeTabOrder(tab_bar, tab, +1); + TabBarQueueReorder(tab_bar, tab, +1); } } }