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; 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() static void NavMoveRequestCancel()
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
@ -2288,7 +2294,7 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con
// Scoring for navigation // Scoring for navigation
if (g.NavId != id && !(item_flags & ImGuiItemFlags_NoNav)) 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 #if IMGUI_DEBUG_NAV_SCORING
// [DEBUG] Score all items in NavWindow at all times // [DEBUG] Score all items in NavWindow at all times
if (!g.NavMoveRequest) if (!g.NavMoveRequest)
@ -2333,8 +2339,9 @@ 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. // 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) // 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; window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask;
if (g.NavWindow == window->NavRootWindow)
if (g.NavId == id || g.NavAnyRequest) 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); NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
} }
@ -2883,9 +2890,14 @@ static void ImGui::NavUpdate()
g.NavJustMovedToId = 0; g.NavJustMovedToId = 0;
// Process navigation move request // 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); IM_ASSERT(g.NavWindow && result->Window);
// Scroll to keep newly navigated item fully into view // 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 // Apply result from previous frame navigation directional move request
ClearActiveID(); ClearActiveID();
g.NavWindow = result->Window;
SetNavIDAndMoveMouse(result->ID, g.NavLayer, result->RectRel); SetNavIDAndMoveMouse(result->ID, g.NavLayer, result->RectRel);
g.NavJustMovedToId = result->ID; g.NavJustMovedToId = result->ID;
g.NavMoveFromClampedRefRect = false; g.NavMoveFromClampedRefRect = false;
@ -2903,7 +2916,7 @@ static void ImGui::NavUpdate()
if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive) if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
{ {
IM_ASSERT(g.NavMoveRequest); IM_ASSERT(g.NavMoveRequest);
if (g.NavMoveResult.ID == 0) if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
g.NavDisableHighlight = false; g.NavDisableHighlight = false;
g.NavMoveRequestForward = ImGuiNavForward_None; g.NavMoveRequestForward = ImGuiNavForward_None;
} }
@ -3061,11 +3074,9 @@ static void ImGui::NavUpdate()
} }
} }
// Reset search // Reset search results
ImGuiNavMoveResult* result = &g.NavMoveResult; g.NavMoveResultLocal.Clear();
result->ID = result->ParentID = 0; g.NavMoveResultOther.Clear();
result->Window = NULL;
result->DistAxial = result->DistBox = result->DistCenter = FLT_MAX;
// 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 // 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) 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) static void NavProcessMoveRequestWrapAround(ImGuiWindow* window)
{ {
ImGuiContext& g = *GImGui; 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) if ((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == 0)
{ {
g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; 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; g.Style.ChildBorderSize = backup_border_size;
// Process navigation-in immediately so NavInit can run on first frame // 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::FocusWindow(child_window);
ImGui::NavInitWindow(child_window, false); ImGui::NavInitWindow(child_window, false);
@ -4972,7 +4983,7 @@ void ImGui::EndChild()
ImGuiWindow* parent_window = GetCurrentWindow(); ImGuiWindow* parent_window = GetCurrentWindow();
ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz); ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
ItemSize(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); ItemAdd(bb, window->ChildId);
RenderNavHighlight(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 // Automatically disable manual moving/resizing when NoInputs is set
if (flags & ImGuiWindowFlags_NoInputs) if (flags & ImGuiWindowFlags_NoInputs)
flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; 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 int current_frame = g.FrameCount;
const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame); 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))) if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
window->RootNonPopupWindow = parent_window->RootNonPopupWindow; window->RootNonPopupWindow = parent_window->RootNonPopupWindow;
window->NavRootWindow = window; window->NavRootWindow = window;
//while (window->NavRootWindow->Flags & ImGuiWindowFlags_NavFlattened) while (window->NavRootWindow->Flags & ImGuiWindowFlags_NavFlattened)
// window->NavRootWindow = window->NavRootWindow->ParentWindow; window->NavRootWindow = window->NavRootWindow->ParentWindow;
window->Active = true; window->Active = true;
window->BeginOrderWithinParent = 0; window->BeginOrderWithinParent = 0;
@ -10861,7 +10873,7 @@ void ImGui::EndMenuBar()
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
// Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings. // 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; ImGuiWindow* nav_earliest_child = g.NavWindow;
while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu)) 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 // Nav: When a left move request within our child menu failed, close the menu
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow; 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); ClosePopupToLevel(g.OpenPopupStack.Size - 1);
NavMoveRequestCancel(); 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_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_NoNavFocus = 1 << 18, // No focusing of this window with gamepad/keyboard navigation
ImGuiWindowFlags_NoNavInputs = 1 << 19, // No gamepad/keyboard navigation within the window 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] // [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_ChildWindow = 1 << 24, // Don't use! For internal use by BeginChild()
ImGuiWindowFlags_Tooltip = 1 << 25, // Don't use! For internal use by BeginTooltip() ImGuiWindowFlags_Tooltip = 1 << 25, // Don't use! For internal use by BeginTooltip()
ImGuiWindowFlags_Popup = 1 << 26, // Don't use! For internal use by BeginPopup() 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("Alpha", &alpha_flags, ImGuiColorEditFlags_AlphaPreview); ImGui::SameLine();
ImGui::RadioButton("Both", &alpha_flags, ImGuiColorEditFlags_AlphaPreviewHalf); 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); ImGui::PushItemWidth(-160);
for (int i = 0; i < ImGuiCol_COUNT; i++) for (int i = 0; i < ImGuiCol_COUNT; i++)
{ {

View File

@ -514,7 +514,8 @@ struct ImGuiNavMoveResult
float DistAxial; float DistAxial;
ImRect RectRel; // Best candidate bounding box in window relative space 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 // 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) 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 NavMoveDir; // Direction of the move request (left/right/up/down)
ImGuiDir NavMoveDirLast; // Direction of the previous move request 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 // Render
ImDrawData DrawData; // Main ImDrawData instance to pass render information to the user ImDrawData DrawData; // Main ImDrawData instance to pass render information to the user