Nav: Added proper version of ImGuiWindowFlags_NavFlattened that handles scrolling nicely. Marked as private as I'm not happy with the name. (#787)

This commit is contained in:
omar 2018-01-29 23:06:55 +01:00
parent b40dc5c4f2
commit c851b33352
4 changed files with 39 additions and 25 deletions

View File

@ -2254,6 +2254,12 @@ static inline void NavUpdateAnyRequestFlag()
g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || IMGUI_DEBUG_NAV_SCORING;
}
static bool NavMoveRequestButNoResultYet()
{
ImGuiContext& g = *GImGui;
return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
}
static void NavMoveRequestCancel()
{
ImGuiContext& g = *GImGui;
@ -2288,7 +2294,7 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con
// Scoring for navigation
if (g.NavId != id && !(item_flags & ImGuiItemFlags_NoNav))
{
ImGuiNavMoveResult* result = &g.NavMoveResult;
ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
#if IMGUI_DEBUG_NAV_SCORING
// [DEBUG] Score all items in NavWindow at all times
if (!g.NavMoveRequest)
@ -2333,9 +2339,10 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg)
// it may not scale very well for windows with ten of thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
// We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick)
window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask;
if (g.NavWindow == window->NavRootWindow)
if (g.NavId == id || g.NavAnyRequest)
NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
if (g.NavId == id || g.NavAnyRequest)
if (g.NavWindow->NavRootWindow == window->NavRootWindow)
if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
}
window->DC.LastItemId = id;
@ -2883,9 +2890,14 @@ static void ImGui::NavUpdate()
g.NavJustMovedToId = 0;
// Process navigation move request
if (g.NavMoveRequest && g.NavMoveResult.ID != 0)
if (g.NavMoveRequest && (g.NavMoveResultLocal.ID != 0 || g.NavMoveResultOther.ID != 0))
{
ImGuiNavMoveResult* result = &g.NavMoveResult;
// Select which result to use
ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
if (g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow) // Maybe entering a flattened child? In this case solve the tie using the regular scoring rules
if ((g.NavMoveResultOther.DistBox < g.NavMoveResultLocal.DistBox) || (g.NavMoveResultOther.DistBox == g.NavMoveResultLocal.DistBox && g.NavMoveResultOther.DistCenter < g.NavMoveResultLocal.DistCenter))
result = &g.NavMoveResultOther;
IM_ASSERT(g.NavWindow && result->Window);
// Scroll to keep newly navigated item fully into view
@ -2894,6 +2906,7 @@ static void ImGui::NavUpdate()
// Apply result from previous frame navigation directional move request
ClearActiveID();
g.NavWindow = result->Window;
SetNavIDAndMoveMouse(result->ID, g.NavLayer, result->RectRel);
g.NavJustMovedToId = result->ID;
g.NavMoveFromClampedRefRect = false;
@ -2903,7 +2916,7 @@ static void ImGui::NavUpdate()
if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
{
IM_ASSERT(g.NavMoveRequest);
if (g.NavMoveResult.ID == 0)
if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
g.NavDisableHighlight = false;
g.NavMoveRequestForward = ImGuiNavForward_None;
}
@ -3061,11 +3074,9 @@ static void ImGui::NavUpdate()
}
}
// Reset search
ImGuiNavMoveResult* result = &g.NavMoveResult;
result->ID = result->ParentID = 0;
result->Window = NULL;
result->DistAxial = result->DistBox = result->DistCenter = FLT_MAX;
// Reset search results
g.NavMoveResultLocal.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 == 0)
@ -4826,7 +4837,7 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags fla
static void NavProcessMoveRequestWrapAround(ImGuiWindow* window)
{
ImGuiContext& g = *GImGui;
if (g.NavMoveRequest && g.NavWindow == window && g.NavMoveResult.ID == 0)
if (g.NavWindow == window && NavMoveRequestButNoResultYet())
if ((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == 0)
{
g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
@ -4928,7 +4939,7 @@ static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, b
g.Style.ChildBorderSize = backup_border_size;
// Process navigation-in immediately so NavInit can run on first frame
if (/*!(flags & ImGuiWindowFlags_NavFlattened) &&*/ (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll) && g.NavActivateId == id)
if (!(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll) && g.NavActivateId == id)
{
ImGui::FocusWindow(child_window);
ImGui::NavInitWindow(child_window, false);
@ -4972,7 +4983,7 @@ void ImGui::EndChild()
ImGuiWindow* parent_window = GetCurrentWindow();
ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
ItemSize(sz);
if (/*!(window->Flags & ImGuiWindowFlags_NavFlattened) &&*/ (window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll))
if (!(window->Flags & ImGuiWindowFlags_NavFlattened) && (window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll))
{
ItemAdd(bb, window->ChildId);
RenderNavHighlight(bb, window->ChildId);
@ -5420,8 +5431,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// Automatically disable manual moving/resizing when NoInputs is set
if (flags & ImGuiWindowFlags_NoInputs)
flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
//if (flags & ImGuiWindowFlags_NavFlattened)
// IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
if (flags & ImGuiWindowFlags_NavFlattened)
IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
const int current_frame = g.FrameCount;
const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
@ -5529,8 +5541,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
window->RootNonPopupWindow = parent_window->RootNonPopupWindow;
window->NavRootWindow = window;
//while (window->NavRootWindow->Flags & ImGuiWindowFlags_NavFlattened)
// window->NavRootWindow = window->NavRootWindow->ParentWindow;
while (window->NavRootWindow->Flags & ImGuiWindowFlags_NavFlattened)
window->NavRootWindow = window->NavRootWindow->ParentWindow;
window->Active = true;
window->BeginOrderWithinParent = 0;
@ -10861,7 +10873,7 @@ void ImGui::EndMenuBar()
ImGuiContext& g = *GImGui;
// Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings.
if (g.NavMoveRequest && g.NavMoveResult.ID == 0 && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
{
ImGuiWindow* nav_earliest_child = g.NavWindow;
while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu))
@ -11029,7 +11041,7 @@ void ImGui::EndMenu()
// Nav: When a left move request within our child menu failed, close the menu
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveRequest && g.NavMoveResult.ID == 0 && g.NavMoveDir == ImGuiDir_Left && window->DC.LayoutType == ImGuiLayoutType_Vertical)
if (g.NavWindow && g.NavWindow->ParentWindow == window && NavMoveRequestButNoResultYet() && g.NavMoveDir == ImGuiDir_Left && window->DC.LayoutType == ImGuiLayoutType_Vertical)
{
ClosePopupToLevel(g.OpenPopupStack.Size - 1);
NavMoveRequestCancel();

View File

@ -552,9 +552,9 @@ enum ImGuiWindowFlags_
ImGuiWindowFlags_ResizeFromAnySide = 1 << 17, // (WIP) Enable resize from any corners and borders. Your back-end needs to honor the different values of io.MouseCursor set by imgui.
ImGuiWindowFlags_NoNavFocus = 1 << 18, // No focusing of this window with gamepad/keyboard navigation
ImGuiWindowFlags_NoNavInputs = 1 << 19, // No gamepad/keyboard navigation within the window
//ImGuiWindowFlags_NavFlattened = 1 << 20, // Allow gamepad/keyboard navigation to cross over parent border to this child (only use on child that have no scrolling!)
// [Internal]
ImGuiWindowFlags_NavFlattened = 1 << 20, // (WIP) Allow gamepad/keyboard navigation to cross over parent border to this child (only use on child that have no scrolling!)
ImGuiWindowFlags_ChildWindow = 1 << 24, // Don't use! For internal use by BeginChild()
ImGuiWindowFlags_Tooltip = 1 << 25, // Don't use! For internal use by BeginTooltip()
ImGuiWindowFlags_Popup = 1 << 26, // Don't use! For internal use by BeginPopup()

View File

@ -2148,7 +2148,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
ImGui::RadioButton("Alpha", &alpha_flags, ImGuiColorEditFlags_AlphaPreview); ImGui::SameLine();
ImGui::RadioButton("Both", &alpha_flags, ImGuiColorEditFlags_AlphaPreviewHalf);
ImGui::BeginChild("#colors", ImVec2(0, 300), true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar);
ImGui::BeginChild("#colors", ImVec2(0, 300), true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NavFlattened);
ImGui::PushItemWidth(-160);
for (int i = 0; i < ImGuiCol_COUNT; i++)
{

View File

@ -514,7 +514,8 @@ struct ImGuiNavMoveResult
float DistAxial;
ImRect RectRel; // Best candidate bounding box in window relative space
ImGuiNavMoveResult() { ID = ParentID = 0; Window = NULL; DistBox = DistCenter = DistAxial = 0.0f; }
ImGuiNavMoveResult() { Clear(); }
void Clear() { ID = ParentID = 0; Window = NULL; DistBox = DistCenter = DistAxial = FLT_MAX; RectRel = ImRect(); }
};
// Storage for SetNexWindow** functions
@ -633,7 +634,8 @@ struct ImGuiContext
ImGuiNavForward NavMoveRequestForward; // None / ForwardQueued / ForwardActive (this is used to navigate sibling parent menus from a child menu)
ImGuiDir NavMoveDir; // Direction of the move request (left/right/up/down)
ImGuiDir NavMoveDirLast; // Direction of the previous move request
ImGuiNavMoveResult NavMoveResult; // Best move request candidate
ImGuiNavMoveResult NavMoveResultLocal; // Best move request candidate within NavWindow
ImGuiNavMoveResult NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using the NavFlattened flag)
// Render
ImDrawData DrawData; // Main ImDrawData instance to pass render information to the user