Merge branch 'master' into docking

# Conflicts:
#	backends/imgui_impl_opengl3.cpp
#	examples/imgui_examples.sln
#	imgui.cpp
#	imgui.h
This commit is contained in:
ocornut
2021-08-30 20:01:16 +02:00
28 changed files with 699 additions and 548 deletions

525
imgui.cpp
View File

@ -1,4 +1,4 @@
// dear imgui, v1.84
// dear imgui, v1.85 WIP
// (main code and documentation)
// Help:
@ -15,7 +15,10 @@
// - Wiki https://github.com/ocornut/imgui/wiki (lots of good stuff there)
// - Glossary https://github.com/ocornut/imgui/wiki/Glossary
// - Issues & support https://github.com/ocornut/imgui/issues
// - Discussions https://github.com/ocornut/imgui/discussions
// Getting Started?
// - For first-time users having issues compiling/linking/running or issues loading fonts:
// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above.
// Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
// See LICENSE.txt for copyright and licensing details (standard MIT License).
@ -386,6 +389,7 @@ CODE
- 2021/XX/XX (1.XX) - Moved IME support functions from io.ImeSetInputScreenPosFn, io.ImeWindowHandle to the PlatformIO api.
- 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead.
- 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019):
- ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList()
- ImFont::GlyphRangesBuilder -> use ImFontGlyphRangesBuilder
@ -919,8 +923,9 @@ namespace ImGui
static void NavUpdate();
static void NavUpdateWindowing();
static void NavUpdateWindowingOverlay();
static void NavUpdateMoveResult();
static void NavUpdateInitResult();
static void NavUpdateCancelRequest();
static void NavUpdateCreateMoveRequest();
static float NavUpdatePageUpPageDown();
static inline void NavUpdateAnyRequestFlag();
static void NavEndFrame();
@ -4004,6 +4009,7 @@ void ImGui::UpdateTabFocus()
void ImGui::UpdateHoveredWindowAndCaptureFlags()
{
ImGuiContext& g = *GImGui;
ImGuiIO& io = g.IO;
g.WindowsHoverPadding = ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_HOVER_PADDING, WINDOWS_HOVER_PADDING));
// Find the window hovered by mouse:
@ -4020,48 +4026,61 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags()
clear_hovered_windows = true;
// Disabled mouse?
if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)
clear_hovered_windows = true;
// We track click ownership. When clicked outside of a window the click is owned by the application and won't report hovering nor request capture even while dragging over our windows afterward.
int mouse_earliest_button_down = -1;
// We track click ownership. When clicked outside of a window the click is owned by the application and
// won't report hovering nor request capture even while dragging over our windows afterward.
const bool has_open_popup = (g.OpenPopupStack.Size > 0);
const bool has_open_modal = (modal_window != NULL);
int mouse_earliest_down = -1;
bool mouse_any_down = false;
for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
{
if (g.IO.MouseClicked[i])
g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (g.OpenPopupStack.Size > 0);
mouse_any_down |= g.IO.MouseDown[i];
if (g.IO.MouseDown[i])
if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
mouse_earliest_button_down = i;
if (io.MouseClicked[i])
{
io.MouseDownOwned[i] = (g.HoveredWindow != NULL) || has_open_popup;
io.MouseDownOwnedUnlessPopupClose[i] = (g.HoveredWindow != NULL) || has_open_modal;
}
mouse_any_down |= io.MouseDown[i];
if (io.MouseDown[i])
if (mouse_earliest_down == -1 || io.MouseClickedTime[i] < io.MouseClickedTime[mouse_earliest_down])
mouse_earliest_down = i;
}
const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
const bool mouse_avail = (mouse_earliest_down == -1) || io.MouseDownOwned[mouse_earliest_down];
const bool mouse_avail_unless_popup_close = (mouse_earliest_down == -1) || io.MouseDownOwnedUnlessPopupClose[mouse_earliest_down];
// If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
// FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
if (!mouse_avail && !mouse_dragging_extern_payload)
clear_hovered_windows = true;
if (clear_hovered_windows)
g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
// Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app)
// Update io.WantCaptureMouse for the user application (true = dispatch mouse info to Dear ImGui only, false = dispatch mouse to Dear ImGui + underlying app)
// Update io.WantCaptureMouseAllowPopupClose (experimental) to give a chance for app to react to popup closure with a drag
if (g.WantCaptureMouseNextFrame != -1)
g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
{
io.WantCaptureMouse = io.WantCaptureMouseUnlessPopupClose = (g.WantCaptureMouseNextFrame != 0);
}
else
g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.OpenPopupStack.Size > 0);
{
io.WantCaptureMouse = (mouse_avail && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_popup;
io.WantCaptureMouseUnlessPopupClose = (mouse_avail_unless_popup_close && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_modal;
}
// Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app)
// Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to Dear ImGui only, false = dispatch keyboard info to Dear ImGui + underlying app)
if (g.WantCaptureKeyboardNextFrame != -1)
g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
else
g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
g.IO.WantCaptureKeyboard = true;
io.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
io.WantCaptureKeyboard = true;
// Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
}
ImGuiKeyModFlags ImGui::GetMergedKeyModFlags()
@ -6687,18 +6706,23 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
}
// Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call.
// Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71)
// When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
// We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child.
// We also disabled this when we have dimming overlay behind this specific one child.
// FIXME: More code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected.
// FIXME: User code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected (github #4493)
const bool is_undocked_or_docked_visible = !window->DockIsActive || window->DockTabIsVisible;
if (is_undocked_or_docked_visible)
{
bool render_decorations_in_parent = false;
if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0)
{
// - We test overlap with the previous child window only (testing all would end up being O(log N) not a good investment here)
// - We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping childs
ImGuiWindow* previous_child = parent_window->DC.ChildWindows.Size >= 2 ? parent_window->DC.ChildWindows[parent_window->DC.ChildWindows.Size - 2] : NULL;
bool previous_child_overlapping = previous_child ? previous_child->Rect().Overlaps(window->Rect()) : false;
bool parent_is_empty = parent_window->DrawList->VtxBuffer.Size > 0;
if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_is_empty && !previous_child_overlapping)
render_decorations_in_parent = true;
}
if (render_decorations_in_parent)
window->DrawList = parent_window->DrawList;
@ -7162,7 +7186,7 @@ void ImGui::PopItemFlag()
}
// BeginDisabled()/EndDisabled()
// - Those can be nested but this cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep things disabled)
// - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled)
// - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently.
// - Feedback welcome at https://github.com/ocornut/imgui/issues/211
// - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it.
@ -7173,7 +7197,10 @@ void ImGui::BeginDisabled(bool disabled)
bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
g.DisabledAlphaBackup = g.Style.Alpha;
if (!was_disabled && disabled)
{
g.DisabledAlphaBackup = g.Style.Alpha;
g.Style.Alpha *= g.Style.DisabledAlpha; // PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha);
}
if (was_disabled || disabled)
g.CurrentItemFlags |= ImGuiItemFlags_Disabled;
g.ItemFlagsStack.push_back(g.CurrentItemFlags);
@ -7185,7 +7212,7 @@ void ImGui::EndDisabled()
bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
//PopItemFlag();
g.ItemFlagsStack.pop_back();
g.CurrentItemFlags &= ~ImGuiItemFlags_Disabled;
g.CurrentItemFlags = g.ItemFlagsStack.back();
if (was_disabled && (g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0)
g.Style.Alpha = g.DisabledAlphaBackup; //PopStyleVar();
}
@ -8048,7 +8075,6 @@ void ImGuiStackSizes::CompareWithCurrentState()
// - GetContentRegionMaxAbs() [Internal]
// - GetContentRegionAvail(),
// - GetWindowContentRegionMin(), GetWindowContentRegionMax()
// - GetWindowContentRegionWidth()
// - BeginGroup()
// - EndGroup()
// Also see in imgui_widgets: tab bars, columns.
@ -8416,12 +8442,6 @@ ImVec2 ImGui::GetWindowContentRegionMax()
return window->ContentRegionRect.Max - window->Pos;
}
float ImGui::GetWindowContentRegionWidth()
{
ImGuiWindow* window = GImGui->CurrentWindow;
return window->ContentRegionRect.GetWidth();
}
// 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()
@ -9074,7 +9094,7 @@ void ImGui::EndPopup()
IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls
IM_ASSERT(g.BeginPopupStack.Size > 0);
// Make all menus and popups wrap around for now, may need to expose that policy.
// Make all menus and popups wrap around for now, may need to expose that policy (e.g. focus scope could include wrap/loop policy flags used by new move requests)
if (g.NavWindow == window)
NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
@ -9587,26 +9607,26 @@ void ImGui::NavMoveRequestCancel()
NavUpdateAnyRequestFlag();
}
void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags)
// Forward will reuse the move request again on the next frame (generally with modifications done to it)
void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None);
IM_ASSERT(g.NavMoveRequestForwardToNextFrame == false);
NavMoveRequestCancel();
g.NavMoveRequestForwardToNextFrame = true;
g.NavMoveDir = move_dir;
g.NavMoveClipDir = clip_dir;
g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
g.NavMoveRequestFlags = move_flags;
g.NavWindow->NavRectRel[g.NavLayer] = bb_rel;
g.NavMoveRequestFlags = move_flags | ImGuiNavMoveFlags_Forwarded;
}
void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
// Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
// popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.
void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags wrap_flags)
{
ImGuiContext& g = *GImGui;
// Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
// popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.
g.NavWrapRequestWindow = window;
g.NavWrapRequestFlags = move_flags;
IM_ASSERT(wrap_flags != 0); // Call with _WrapX, _WrapY, _LoopX, _LoopY
if (g.NavWindow == window && g.NavMoveRequest && g.NavLayer == ImGuiNavLayer_Main)
g.NavMoveRequestFlags |= wrap_flags;
}
// FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
@ -9756,16 +9776,14 @@ static void ImGui::NavUpdate()
ImGuiIO& io = g.IO;
io.WantSetMousePos = false;
g.NavWrapRequestWindow = NULL;
g.NavWrapRequestFlags = ImGuiNavMoveFlags_None;
#if 0
if (g.NavScoringCount > 0) IMGUI_DEBUG_LOG("NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest);
#endif
// 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 = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
if (nav_gamepad_active && g.NavInputSource != ImGuiInputSource_Gamepad)
{
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
@ -9804,16 +9822,8 @@ static void ImGui::NavUpdate()
// Process navigation move request
if (g.NavMoveRequest)
NavUpdateMoveResult();
// When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
{
IM_ASSERT(g.NavMoveRequest);
if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
g.NavDisableHighlight = false;
g.NavMoveRequestForward = ImGuiNavForward_None;
}
NavMoveRequestApplyResult();
g.NavMoveRequest = false;
// Apply application mouse position movement, after we had a chance to process move request result.
if (g.NavMousePosDirty && g.NavIdIsAlive)
@ -9831,7 +9841,7 @@ static void ImGui::NavUpdate()
g.NavJustTabbedId = 0;
IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
// Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0
// Store our return window (for returning from Menu Layer to Main Layer) and clear it as soon as we step back in our own Layer 0
if (g.NavWindow)
NavSaveLastChildNavWindowIntoParent(g.NavWindow);
if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main)
@ -9845,43 +9855,7 @@ static void ImGui::NavUpdate()
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))
{
IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n");
if (g.ActiveId != 0)
{
if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel))
ClearActiveID();
}
else if (g.NavLayer != ImGuiNavLayer_Main)
{
// Leave the "menu" layer
NavRestoreLayer(ImGuiNavLayer_Main);
}
else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
{
// Exit child window
ImGuiWindow* child_window = g.NavWindow;
ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
IM_ASSERT(child_window->ChildId != 0);
ImRect child_rect = child_window->Rect();
FocusWindow(parent_window);
SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, ImRect(child_rect.Min - parent_window->Pos, child_rect.Max - parent_window->Pos));
}
else if (g.OpenPopupStack.Size > 0)
{
// Close open popup/menu
if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
}
else
{
// Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
g.NavWindow->NavLastIds[0] = 0;
g.NavId = g.NavFocusScopeId = 0;
}
}
NavUpdateCancelRequest();
// Process manual activation request
g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
@ -9902,59 +9876,14 @@ static void ImGui::NavUpdate()
g.NavDisableHighlight = true;
if (g.NavActivateId != 0)
IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
g.NavMoveRequest = false;
// Process programmatic activation request
if (g.NavNextActivateId != 0)
g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
g.NavNextActivateId = 0;
// Initiate directional inputs request
if (g.NavMoveRequestForward == ImGuiNavForward_None)
{
g.NavMoveDir = ImGuiDir_None;
g.NavMoveRequestFlags = ImGuiNavMoveFlags_None;
if (g.NavWindow && !g.NavWindowingTarget && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
{
const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat;
if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsNavInputTest(ImGuiNavInput_DpadLeft, read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_, read_mode))) { g.NavMoveDir = ImGuiDir_Left; }
if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; }
if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && (IsNavInputTest(ImGuiNavInput_DpadUp, read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_, read_mode))) { g.NavMoveDir = ImGuiDir_Up; }
if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; }
}
g.NavMoveClipDir = g.NavMoveDir;
}
else
{
// Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window)
// (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function)
IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir);
g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
}
// Update PageUp/PageDown/Home/End scroll
// FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
float nav_scoring_rect_offset_y = 0.0f;
if (nav_keyboard_active)
nav_scoring_rect_offset_y = NavUpdatePageUpPageDown();
// If we initiate a movement request and have no current NavId, we initiate a InitDefautRequest that will be used as a fallback if the direction fails to find a match
if (g.NavMoveDir != ImGuiDir_None)
{
g.NavMoveRequest = true;
g.NavMoveRequestKeyMods = io.KeyMods;
g.NavMoveDirLast = g.NavMoveDir;
}
if (g.NavMoveRequest && g.NavId == 0)
{
IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer);
g.NavInitRequest = g.NavInitRequestFromMove = true;
// Reassigning with same value, we're being explicit here.
g.NavInitResultId = 0; // -V1048
g.NavDisableHighlight = false;
}
// Process move requests
NavUpdateCreateMoveRequest();
NavUpdateAnyRequestFlag();
// Scrolling
@ -9963,7 +9892,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 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.
if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest) //-V560
{
if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
@ -9980,36 +9909,7 @@ static void ImGui::NavUpdate()
SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
}
// Reset search results
g.NavMoveResultLocal.Clear();
g.NavMoveResultLocalVisibleSet.Clear();
g.NavMoveResultOther.Clear();
// 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_Gamepad && 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));
if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
{
IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel\n");
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].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);
g.NavScoringRect = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : ImRect(0, 0, 0, 0);
g.NavScoringRect.TranslateY(nav_scoring_rect_offset_y);
g.NavScoringRect.Min.x = ImMin(g.NavScoringRect.Min.x + 1.0f, g.NavScoringRect.Max.x);
g.NavScoringRect.Max.x = g.NavScoringRect.Min.x;
IM_ASSERT(!g.NavScoringRect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem().
//GetForegroundDrawList()->AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
// [DEBUG]
g.NavScoringCount = 0;
#if IMGUI_DEBUG_NAV_RECTS
if (g.NavWindow)
@ -10039,10 +9939,100 @@ static void ImGui::NavUpdateInitResult()
}
}
// Apply result from previous frame navigation directional move request
static void ImGui::NavUpdateMoveResult()
void ImGui::NavUpdateCreateMoveRequest()
{
ImGuiContext& g = *GImGui;
ImGuiIO& io = g.IO;
ImGuiWindow* window = g.NavWindow;
if (g.NavMoveRequestForwardToNextFrame)
{
// Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window)
// (preserve most state, which were already set by the NavMoveRequestForward() function)
IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
IM_ASSERT(g.NavMoveRequestFlags & ImGuiNavMoveFlags_Forwarded);
IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir);
g.NavMoveRequestForwardToNextFrame = false;
}
else
{
// Initiate directional inputs request
g.NavMoveDir = ImGuiDir_None;
g.NavMoveRequestFlags = ImGuiNavMoveFlags_None;
if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs))
{
const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat;
if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsNavInputTest(ImGuiNavInput_DpadLeft, read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_, read_mode))) { g.NavMoveDir = ImGuiDir_Left; }
if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; }
if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && (IsNavInputTest(ImGuiNavInput_DpadUp, read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_, read_mode))) { g.NavMoveDir = ImGuiDir_Up; }
if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; }
}
g.NavMoveClipDir = g.NavMoveDir;
}
// Update PageUp/PageDown/Home/End scroll
// FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
float nav_scoring_rect_offset_y = 0.0f;
if (nav_keyboard_active)
nav_scoring_rect_offset_y = NavUpdatePageUpPageDown();
// If we initiate a movement request and have no current NavId, we initiate a InitDefaultRequest that will be used as a fallback if the direction fails to find a match
if (g.NavMoveDir != ImGuiDir_None)
{
IM_ASSERT(window != NULL);
g.NavMoveRequest = true;
g.NavMoveRequestKeyMods = io.KeyMods;
g.NavMoveDirLast = g.NavMoveDir;
g.NavMoveResultLocal.Clear();
g.NavMoveResultLocalVisibleSet.Clear();
g.NavMoveResultOther.Clear();
}
// Moving with no reference triggers a init request
if (g.NavMoveRequest && g.NavId == 0)
{
IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer);
g.NavInitRequest = g.NavInitRequestFromMove = true;
g.NavInitResultId = 0;
g.NavDisableHighlight = false;
}
// 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_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL)
{
ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1));
if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
{
IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel\n");
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].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)
g.NavScoringRect = ImRect();
if (window)
{
ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);
g.NavScoringRect = ImRect(window->Pos + nav_rect_rel.Min, window->Pos + nav_rect_rel.Max);
g.NavScoringRect.TranslateY(nav_scoring_rect_offset_y);
g.NavScoringRect.Min.x = ImMin(g.NavScoringRect.Min.x + 1.0f, g.NavScoringRect.Max.x);
g.NavScoringRect.Max.x = g.NavScoringRect.Min.x;
IM_ASSERT(!g.NavScoringRect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem().
//GetForegroundDrawList()->AddRect(g.NavScoringRect.Min, g.NavScoringRect.Max, IM_COL32(255,200,0,255)); // [DEBUG]
}
}
// Apply result from previous frame navigation directional move request. Always called from NavUpdate()
void ImGui::NavMoveRequestApplyResult()
{
ImGuiContext& g = *GImGui;
if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
{
// In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result)
@ -10104,7 +10094,54 @@ static void ImGui::NavUpdateMoveResult()
g.NavDisableMouseHover = g.NavMousePosDirty = true;
}
// Process NavCancel input (to close a popup, get back to parent, clear focus)
// FIXME: In order to support e.g. Escape to clear a selection we'll need:
// - either to store the equivalent of ActiveIdUsingKeyInputMask for a FocusScope and test for it.
// - either to move most/all of those tests to the epilogue/end functions of the scope they are dealing with (e.g. exit child window in EndChild()) or in EndFrame(), to allow an earlier intercept
static void ImGui::NavUpdateCancelRequest()
{
ImGuiContext& g = *GImGui;
if (!IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
return;
IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n");
if (g.ActiveId != 0)
{
if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel))
ClearActiveID();
}
else if (g.NavLayer != ImGuiNavLayer_Main)
{
// Leave the "menu" layer
NavRestoreLayer(ImGuiNavLayer_Main);
}
else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
{
// Exit child window
ImGuiWindow* child_window = g.NavWindow;
ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
IM_ASSERT(child_window->ChildId != 0);
ImRect child_rect = child_window->Rect();
FocusWindow(parent_window);
SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, ImRect(child_rect.Min - parent_window->Pos, child_rect.Max - parent_window->Pos));
}
else if (g.OpenPopupStack.Size > 0)
{
// Close open popup/menu
if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
}
else
{
// Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
g.NavWindow->NavLastIds[0] = 0;
g.NavId = g.NavFocusScopeId = 0;
}
}
// Handle PageUp/PageDown/Home/End keys
// FIXME-NAV: how to get Home/End to aim at the beginning/end of a 2D grid?
static float ImGui::NavUpdatePageUpPageDown()
{
ImGuiContext& g = *GImGui;
@ -10120,60 +10157,60 @@ static float ImGui::NavUpdatePageUpPageDown()
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 (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out
return 0.0f;
if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll)
{
if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll)
// Fallback manual-scroll when window has no navigable item
if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
else if (home_pressed)
SetScrollY(window, 0.0f);
else if (end_pressed)
SetScrollY(window, window->ScrollMax.y);
}
else
{
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(io.KeyMap[ImGuiKey_PageUp], true))
{
// Fallback manual-scroll when window has no navigable item
if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
else if (home_pressed)
SetScrollY(window, 0.0f);
else if (end_pressed)
SetScrollY(window, window->ScrollMax.y);
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
else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
{
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(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(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)
g.NavMoveClipDir = ImGuiDir_Down;
g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
}
else if (home_pressed)
{
// FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
// Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result.
// Preserve current horizontal position if we have any.
nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y;
if (nav_rect_rel.IsInverted())
nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
g.NavMoveDir = ImGuiDir_Down;
g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
}
else if (end_pressed)
{
nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y;
if (nav_rect_rel.IsInverted())
nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
g.NavMoveDir = ImGuiDir_Up;
g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
}
return nav_scoring_rect_offset_y;
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)
g.NavMoveClipDir = ImGuiDir_Down;
g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
}
else if (home_pressed)
{
// FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
// Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result.
// Preserve current horizontal position if we have any.
nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y;
if (nav_rect_rel.IsInverted())
nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
g.NavMoveDir = ImGuiDir_Down;
g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
}
else if (end_pressed)
{
nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y;
if (nav_rect_rel.IsInverted())
nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
g.NavMoveDir = ImGuiDir_Up;
g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
}
return nav_scoring_rect_offset_y;
}
return 0.0f;
}
@ -10187,13 +10224,14 @@ static void ImGui::NavEndFrame()
NavUpdateWindowingOverlay();
// Perform wrap-around in menus
ImGuiWindow* window = g.NavWrapRequestWindow;
ImGuiNavMoveFlags move_flags = g.NavWrapRequestFlags;
if (window != NULL && g.NavWindow == window && NavMoveRequestButNoResultYet() && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == ImGuiNavLayer_Main)
// FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame.
ImGuiWindow* window = g.NavWindow;
const ImGuiNavMoveFlags move_flags = g.NavMoveRequestFlags;
const ImGuiNavMoveFlags wanted_flags = ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY;
if (window && NavMoveRequestButNoResultYet() && (g.NavMoveRequestFlags & wanted_flags) && (g.NavMoveRequestFlags & ImGuiNavMoveFlags_Forwarded) == 0)
{
IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
ImRect bb_rel = window->NavRectRel[0];
bool do_forward = false;
ImRect bb_rel = window->NavRectRel[g.NavLayer];
ImGuiDir clip_dir = g.NavMoveDir;
if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
{
@ -10204,7 +10242,7 @@ static void ImGui::NavEndFrame()
bb_rel.TranslateY(-bb_rel.GetHeight());
clip_dir = ImGuiDir_Up;
}
NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
do_forward = true;
}
if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
{
@ -10214,7 +10252,7 @@ static void ImGui::NavEndFrame()
bb_rel.TranslateY(+bb_rel.GetHeight());
clip_dir = ImGuiDir_Down;
}
NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
do_forward = true;
}
if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
{
@ -10225,7 +10263,7 @@ static void ImGui::NavEndFrame()
bb_rel.TranslateX(-bb_rel.GetWidth());
clip_dir = ImGuiDir_Left;
}
NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
do_forward = true;
}
if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
{
@ -10235,7 +10273,12 @@ static void ImGui::NavEndFrame()
bb_rel.TranslateX(+bb_rel.GetWidth());
clip_dir = ImGuiDir_Right;
}
NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
do_forward = true;
}
if (do_forward)
{
window->NavRectRel[g.NavLayer] = bb_rel;
NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags);
}
}
}