Nav: Draft internal api to forward move request with loop/wrap options. Will rework for parallel scoring of two paths (as a generalization of the NavFlattened concept). (#787)

This commit is contained in:
omar 2018-05-14 20:06:41 +02:00
parent e11610d6ff
commit 99ff6fc7e4
2 changed files with 70 additions and 14 deletions

View File

@ -2251,7 +2251,7 @@ static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
// We perform scoring on items bounding box clipped by the current clipping rectangle on the other axis (clipping on our movement axis would give us equal scores for all clipped items) // We perform scoring on items bounding box clipped by the current clipping rectangle on the other axis (clipping on our movement axis would give us equal scores for all clipped items)
// For example, this ensure that items in one column are not reached when moving vertically from items in another column. // For example, this ensure that items in one column are not reached when moving vertically from items in another column.
NavClampRectToVisibleAreaForMoveDir(g.NavMoveDir, cand, window->ClipRect); NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect);
// Compute distance between boxes // Compute distance between boxes
// FIXME-NAV: Introducing biases for vertical navigation, needs to be removed. // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
@ -3227,6 +3227,7 @@ static void ImGui::NavUpdate()
if (g.NavMoveRequestForward == ImGuiNavForward_None) if (g.NavMoveRequestForward == ImGuiNavForward_None)
{ {
g.NavMoveDir = ImGuiDir_None; g.NavMoveDir = ImGuiDir_None;
g.NavMoveRequestFlags = 0;
if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
{ {
if ((allowed_dir_flags & (1<<ImGuiDir_Left)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadLeft, ImGuiNavInput_KeyLeft_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Left; if ((allowed_dir_flags & (1<<ImGuiDir_Left)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadLeft, ImGuiNavInput_KeyLeft_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Left;
@ -3234,11 +3235,13 @@ static void ImGui::NavUpdate()
if ((allowed_dir_flags & (1<<ImGuiDir_Up)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadUp, ImGuiNavInput_KeyUp_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Up; if ((allowed_dir_flags & (1<<ImGuiDir_Up)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadUp, ImGuiNavInput_KeyUp_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Up;
if ((allowed_dir_flags & (1<<ImGuiDir_Down)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadDown, ImGuiNavInput_KeyDown_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Down; if ((allowed_dir_flags & (1<<ImGuiDir_Down)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadDown, ImGuiNavInput_KeyDown_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Down;
} }
g.NavMoveClipDir = g.NavMoveDir;
} }
else 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) // 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)
IM_ASSERT(g.NavMoveDir != ImGuiDir_None); // (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); IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
g.NavMoveRequestForward = ImGuiNavForward_ForwardActive; g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
} }
@ -4547,9 +4550,11 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items
const ImVec2 pos = window->DC.CursorPos; const ImVec2 pos = window->DC.CursorPos;
int start = (int)((window->ClipRect.Min.y - pos.y) / items_height); int start = (int)((window->ClipRect.Min.y - pos.y) / items_height);
int end = (int)((window->ClipRect.Max.y - pos.y) / items_height); int end = (int)((window->ClipRect.Max.y - pos.y) / items_height);
if (g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Up) // When performing a navigation request, ensure we have one item extra in the direction we are moving to
// 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)
start--; start--;
if (g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down)
end++; end++;
start = ImClamp(start, 0, items_count); start = ImClamp(start, 0, items_count);
@ -5101,15 +5106,50 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags fla
return is_open; return is_open;
} }
static void NavProcessMoveRequestWrapAround(ImGuiWindow* window) void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
if (g.NavWindow == window && NavMoveRequestButNoResultYet()) IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None);
if ((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == 0)
{
g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
ImGui::NavMoveRequestCancel(); ImGui::NavMoveRequestCancel();
g.NavWindow->NavRectRel[0].Min.y = g.NavWindow->NavRectRel[0].Max.y = ((g.NavMoveDir == ImGuiDir_Up) ? ImMax(window->SizeFull.y, window->SizeContents.y) : 0.0f) - window->Scroll.y; g.NavMoveDir = move_dir;
g.NavMoveClipDir = clip_dir;
g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
g.NavMoveRequestFlags = move_flags;
g.NavWindow->NavRectRel[g.NavLayer] = bb_rel;
}
void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
{
ImGuiContext& g = *GImGui;
if (g.NavWindow != window || !NavMoveRequestButNoResultYet() || g.NavMoveRequestForward != ImGuiNavForward_None || g.NavLayer != 0)
return;
IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
ImRect bb_rel = window->NavRectRel[0];
ImGuiDir clip_dir = g.NavMoveDir;
if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
{
bb_rel.Min.x = bb_rel.Max.x = ImMax(window->SizeFull.x, window->SizeContents.x) - window->Scroll.x;
if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(-bb_rel.GetHeight()); clip_dir = ImGuiDir_Up; }
NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
}
if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
{
bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(+bb_rel.GetHeight()); clip_dir = ImGuiDir_Down; }
NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
}
if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
{
bb_rel.Min.y = bb_rel.Max.y = ImMax(window->SizeFull.y, window->SizeContents.y) - window->Scroll.y;
if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(-bb_rel.GetWidth()); clip_dir = ImGuiDir_Left; }
NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
}
if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
{
bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(+bb_rel.GetWidth()); clip_dir = ImGuiDir_Right; }
NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
} }
} }
@ -5120,7 +5160,7 @@ void ImGui::EndPopup()
IM_ASSERT(g.CurrentPopupStack.Size > 0); IM_ASSERT(g.CurrentPopupStack.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.
NavProcessMoveRequestWrapAround(g.CurrentWindow); NavMoveRequestTryWrapping(g.CurrentWindow, ImGuiNavMoveFlags_LoopY);
End(); End();
} }

View File

@ -50,6 +50,7 @@ typedef int ImGuiItemFlags; // flags: for PushItemFlag()
typedef int ImGuiItemStatusFlags; // flags: storage for DC.LastItemXXX // enum ImGuiItemStatusFlags_ typedef int ImGuiItemStatusFlags; // flags: storage for DC.LastItemXXX // enum ImGuiItemStatusFlags_
typedef int ImGuiNavHighlightFlags; // flags: for RenderNavHighlight() // enum ImGuiNavHighlightFlags_ typedef int ImGuiNavHighlightFlags; // flags: for RenderNavHighlight() // enum ImGuiNavHighlightFlags_
typedef int ImGuiNavDirSourceFlags; // flags: for GetNavInputAmount2d() // enum ImGuiNavDirSourceFlags_ typedef int ImGuiNavDirSourceFlags; // flags: for GetNavInputAmount2d() // enum ImGuiNavDirSourceFlags_
typedef int ImGuiNavMoveFlags; // flags: for navigation requests // enum ImGuiNavMoveFlags_
typedef int ImGuiSeparatorFlags; // flags: for Separator() - internal // enum ImGuiSeparatorFlags_ typedef int ImGuiSeparatorFlags; // flags: for Separator() - internal // enum ImGuiSeparatorFlags_
typedef int ImGuiSliderFlags; // flags: for SliderBehavior() // enum ImGuiSliderFlags_ typedef int ImGuiSliderFlags; // flags: for SliderBehavior() // enum ImGuiSliderFlags_
@ -302,6 +303,14 @@ enum ImGuiNavDirSourceFlags_
ImGuiNavDirSourceFlags_PadLStick = 1 << 2 ImGuiNavDirSourceFlags_PadLStick = 1 << 2
}; };
enum ImGuiNavMoveFlags_
{
ImGuiNavMoveFlags_LoopX = 1 << 0, // On failed request, restart from opposite side
ImGuiNavMoveFlags_LoopY = 1 << 1,
ImGuiNavMoveFlags_WrapX = 1 << 2, // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left)
ImGuiNavMoveFlags_WrapY = 1 << 3 // This is not super useful for provided for completeness
};
enum ImGuiNavForward enum ImGuiNavForward
{ {
ImGuiNavForward_None, ImGuiNavForward_None,
@ -337,6 +346,8 @@ struct IMGUI_API ImRect
void Expand(const float amount) { Min.x -= amount; Min.y -= amount; Max.x += amount; Max.y += amount; } void Expand(const float amount) { Min.x -= amount; Min.y -= amount; Max.x += amount; Max.y += amount; }
void Expand(const ImVec2& amount) { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; } void Expand(const ImVec2& amount) { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; }
void Translate(const ImVec2& d) { Min.x += d.x; Min.y += d.y; Max.x += d.x; Max.y += d.y; } void Translate(const ImVec2& d) { Min.x += d.x; Min.y += d.y; Max.x += d.x; Max.y += d.y; }
void TranslateX(float dx) { Min.x += dx; Max.x += dx; }
void TranslateY(float dy) { Min.y += dy; Max.y += dy; }
void ClipWith(const ImRect& r) { Min = ImMax(Min, r.Min); Max = ImMin(Max, r.Max); } // Simple version, may lead to an inverted rectangle, which is fine for Contains/Overlaps test but not for display. void ClipWith(const ImRect& r) { Min = ImMax(Min, r.Min); Max = ImMin(Max, r.Max); } // Simple version, may lead to an inverted rectangle, which is fine for Contains/Overlaps test but not for display.
void ClipWithFull(const ImRect& r) { Min = ImClamp(Min, r.Min, r.Max); Max = ImClamp(Max, r.Min, r.Max); } // Full version, ensure both points are fully clipped. void ClipWithFull(const ImRect& r) { Min = ImClamp(Min, r.Min, r.Max); Max = ImClamp(Max, r.Min, r.Max); } // Full version, ensure both points are fully clipped.
void Floor() { Min.x = (float)(int)Min.x; Min.y = (float)(int)Min.y; Max.x = (float)(int)Max.x; Max.y = (float)(int)Max.y; } void Floor() { Min.x = (float)(int)Min.x; Min.y = (float)(int)Min.y; Max.x = (float)(int)Max.x; Max.y = (float)(int)Max.y; }
@ -645,8 +656,10 @@ struct ImGuiContext
ImRect NavInitResultRectRel; ImRect NavInitResultRectRel;
bool NavMoveFromClampedRefRect; // Set by manual scrolling, if we scroll to a point where NavId isn't visible we reset navigation from visible items 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 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) ImGuiNavForward NavMoveRequestForward; // None / ForwardQueued / ForwardActive (this is used to navigate sibling parent menus from a child menu)
ImGuiDir NavMoveDir, NavMoveDirLast; // Direction of the move request (left/right/up/down), direction of the previous move request ImGuiDir NavMoveDir, NavMoveDirLast; // Direction of the move request (left/right/up/down), direction of the previous move request
ImGuiDir NavMoveClipDir;
ImGuiNavMoveResult NavMoveResultLocal; // Best move request candidate within NavWindow ImGuiNavMoveResult NavMoveResultLocal; // Best move request candidate within NavWindow
ImGuiNavMoveResult NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using the NavFlattened flag) ImGuiNavMoveResult NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using the NavFlattened flag)
@ -762,8 +775,9 @@ struct ImGuiContext
NavInitResultId = 0; NavInitResultId = 0;
NavMoveFromClampedRefRect = false; NavMoveFromClampedRefRect = false;
NavMoveRequest = false; NavMoveRequest = false;
NavMoveRequestFlags = 0;
NavMoveRequestForward = ImGuiNavForward_None; NavMoveRequestForward = ImGuiNavForward_None;
NavMoveDir = NavMoveDirLast = ImGuiDir_None; NavMoveDir = NavMoveDirLast = NavMoveClipDir = ImGuiDir_None;
ModalWindowDarkeningRatio = 0.0f; ModalWindowDarkeningRatio = 0.0f;
OverlayDrawList._Data = &DrawListSharedData; OverlayDrawList._Data = &DrawListSharedData;
@ -1069,6 +1083,8 @@ namespace ImGui
IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit); IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit);
IMGUI_API void NavMoveRequestCancel(); IMGUI_API void NavMoveRequestCancel();
IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags);
IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags);
IMGUI_API void ActivateItem(ImGuiID id); // Remotely activate a button, checkbox, tree node etc. given its unique ID. activation is queued and processed on the next frame when the item is encountered again. IMGUI_API void ActivateItem(ImGuiID id); // Remotely activate a button, checkbox, tree node etc. given its unique ID. activation is queued and processed on the next frame when the item is encountered again.
IMGUI_API float GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode); IMGUI_API float GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode);