mirror of
https://github.com/Drezil/imgui.git
synced 2025-07-04 12:08:47 +02:00
Nav: Added support for PageUp/PageDown (explorer-style: first aim at bottom/top most item, when scroll a page worth of contents). (#787)
This commit is contained in:
74
imgui.cpp
74
imgui.cpp
@ -2538,7 +2538,7 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con
|
||||
|
||||
// Process Move Request (scoring for navigation)
|
||||
// FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
|
||||
if (g.NavId != id && !(item_flags & ImGuiItemFlags_NoNav))
|
||||
if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & ImGuiItemFlags_NoNav))
|
||||
{
|
||||
ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
|
||||
#if IMGUI_DEBUG_NAV_SCORING
|
||||
@ -2555,6 +2555,17 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con
|
||||
result->Window = window;
|
||||
result->RectRel = nav_bb_rel;
|
||||
}
|
||||
|
||||
const float VISIBLE_RATIO = 0.70f;
|
||||
if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
|
||||
if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO)
|
||||
if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb))
|
||||
{
|
||||
result = &g.NavMoveResultLocalVisibleSet;
|
||||
result->ID = id;
|
||||
result->Window = window;
|
||||
result->RectRel = nav_bb_rel;
|
||||
}
|
||||
}
|
||||
|
||||
// Update window-relative bounding box of navigated item
|
||||
@ -3198,9 +3209,14 @@ static void ImGui::NavUpdate()
|
||||
// Select which result to use
|
||||
ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
|
||||
|
||||
// Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules
|
||||
if (g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
|
||||
if ((g.NavMoveResultOther.DistBox < g.NavMoveResultLocal.DistBox) || (g.NavMoveResultOther.DistBox == g.NavMoveResultLocal.DistBox && g.NavMoveResultOther.DistCenter < g.NavMoveResultLocal.DistCenter))
|
||||
// PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
|
||||
if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
|
||||
if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId)
|
||||
result = &g.NavMoveResultLocalVisibleSet;
|
||||
|
||||
// Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
|
||||
if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
|
||||
if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
|
||||
result = &g.NavMoveResultOther;
|
||||
IM_ASSERT(g.NavWindow && result->Window);
|
||||
|
||||
@ -3354,6 +3370,45 @@ static void ImGui::NavUpdate()
|
||||
g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
|
||||
}
|
||||
|
||||
// PageUp/PageDown scroll
|
||||
float nav_scoring_rect_offset_y = 0.0f;
|
||||
if (nav_keyboard_active && g.NavMoveDir == ImGuiDir_None && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget && g.NavLayer == 0)
|
||||
{
|
||||
ImGuiWindow* window = g.NavWindow;
|
||||
bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && (allowed_dir_flags & (1 << ImGuiDir_Up));
|
||||
bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && (allowed_dir_flags & (1 << ImGuiDir_Down));
|
||||
if ((page_up_held && !page_down_held) || (page_down_held && !page_up_held))
|
||||
{
|
||||
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))
|
||||
SetWindowScrollY(window, window->Scroll.y - window->InnerClipRect.GetHeight());
|
||||
else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
|
||||
SetWindowScrollY(window, window->Scroll.y + window->InnerClipRect.GetHeight());
|
||||
}
|
||||
else
|
||||
{
|
||||
const ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
|
||||
const float page_offset_y = ImMax(0.0f, window->InnerClipRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
|
||||
if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
|
||||
{
|
||||
nav_scoring_rect_offset_y = -page_offset_y;
|
||||
g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset, we intentionally request the opposite 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))
|
||||
{
|
||||
nav_scoring_rect_offset_y = +page_offset_y;
|
||||
g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset, we intentionally request the opposite direction (so we can always land on the last item)
|
||||
g.NavMoveClipDir = ImGuiDir_Down;
|
||||
g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (g.NavMoveDir != ImGuiDir_None)
|
||||
{
|
||||
g.NavMoveRequest = true;
|
||||
@ -3401,6 +3456,7 @@ static void ImGui::NavUpdate()
|
||||
|
||||
// Reset search results
|
||||
g.NavMoveResultLocal.Clear();
|
||||
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
|
||||
@ -3421,6 +3477,7 @@ static void ImGui::NavUpdate()
|
||||
// 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.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect();
|
||||
g.NavScoringRectScreen.TranslateY(nav_scoring_rect_offset_y);
|
||||
g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x);
|
||||
g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x;
|
||||
IM_ASSERT(!g.NavScoringRectScreen.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem().
|
||||
@ -4658,9 +4715,14 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items
|
||||
return;
|
||||
}
|
||||
|
||||
// We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect
|
||||
ImRect unclipped_rect = window->ClipRect;
|
||||
if (g.NavMoveRequest)
|
||||
unclipped_rect.Add(g.NavScoringRectScreen);
|
||||
|
||||
const ImVec2 pos = window->DC.CursorPos;
|
||||
int start = (int)((window->ClipRect.Min.y - pos.y) / items_height);
|
||||
int end = (int)((window->ClipRect.Max.y - pos.y) / items_height);
|
||||
int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
|
||||
int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
|
||||
|
||||
// When performing a navigation request, ensure we have one item extra in the direction we are moving to
|
||||
if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up)
|
||||
|
Reference in New Issue
Block a user