Internal: added ScrollToItem() upcoming replacement to ScrollToBringRectIntoView() + ImGuiScrollFlags (WIP) (#3692, #3208, #2812, #4242, #2900)

This commit is contained in:
ocornut
2021-09-16 20:24:47 +02:00
parent fc4988ffb0
commit 8f495e5543
5 changed files with 109 additions and 31 deletions

102
imgui.cpp
View File

@ -8008,32 +8008,80 @@ static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
return scroll;
}
void ImGui::ScrollToItem(ImGuiScrollFlags flags)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
ScrollToRectEx(window, g.LastItemData.NavRect, flags);
}
void ImGui::ScrollToRect(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags)
{
ScrollToRectEx(window, item_rect, flags);
}
// Scroll to keep newly navigated item fully into view
ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect)
ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags)
{
ImGuiContext& g = *GImGui;
ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));
//GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
ImVec2 delta_scroll;
if (!window_rect.Contains(item_rect))
{
if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x)
SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x - g.Style.ItemSpacing.x, 0.0f);
else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x)
SetScrollFromPosX(window, item_rect.Max.x - window->Pos.x + g.Style.ItemSpacing.x, 1.0f);
if (item_rect.Min.y < window_rect.Min.y)
SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y - g.Style.ItemSpacing.y, 0.0f);
else if (item_rect.Max.y >= window_rect.Max.y)
SetScrollFromPosY(window, item_rect.Max.y - window->Pos.y + g.Style.ItemSpacing.y, 1.0f);
// Check that only one behavior is selected per axis
IM_ASSERT((flags & ImGuiScrollFlags_MaskX_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskX_));
IM_ASSERT((flags & ImGuiScrollFlags_MaskY_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskY_));
ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
delta_scroll = next_scroll - window->Scroll;
// Defaults
ImGuiScrollFlags in_flags = flags;
if ((flags & ImGuiScrollFlags_MaskX_) == 0 && window->ScrollbarX)
flags |= ImGuiScrollFlags_KeepVisibleEdgeX;
if ((flags & ImGuiScrollFlags_MaskY_) == 0)
flags |= window->Appearing ? ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeY;
const bool fully_visible_x = item_rect.Min.x >= window_rect.Min.x && item_rect.Max.x <= window_rect.Max.x;
const bool fully_visible_y = item_rect.Min.y >= window_rect.Min.y && item_rect.Max.y <= window_rect.Max.y;
const bool can_be_fully_visible_x = item_rect.GetWidth() <= window_rect.GetWidth();
const bool can_be_fully_visible_y = item_rect.GetHeight() <= window_rect.GetHeight();
if ((flags & ImGuiScrollFlags_KeepVisibleEdgeX) && !fully_visible_x)
{
if (item_rect.Min.x < window_rect.Min.x || !can_be_fully_visible_x)
SetScrollFromPosX(window, item_rect.Min.x - g.Style.ItemSpacing.x - window->Pos.x, 0.0f);
else if (item_rect.Max.x >= window_rect.Max.x)
SetScrollFromPosX(window, item_rect.Max.x + g.Style.ItemSpacing.x - window->Pos.x, 1.0f);
}
else if (((flags & ImGuiScrollFlags_KeepVisibleCenterX) && !fully_visible_x) || (flags & ImGuiScrollFlags_AlwaysCenterX))
{
float target_x = can_be_fully_visible_x ? ImFloor((item_rect.Min.x + item_rect.Max.x - window->InnerRect.GetWidth()) * 0.5f) : item_rect.Min.x;
SetScrollFromPosX(window, target_x - window->Pos.x, 0.0f);
}
if ((flags & ImGuiScrollFlags_KeepVisibleEdgeY) && !fully_visible_y)
{
if (item_rect.Min.y < window_rect.Min.y || !can_be_fully_visible_y)
SetScrollFromPosY(window, item_rect.Min.y - g.Style.ItemSpacing.y - window->Pos.y, 0.0f);
else if (item_rect.Max.y >= window_rect.Max.y)
SetScrollFromPosY(window, item_rect.Max.y + g.Style.ItemSpacing.y - window->Pos.y, 1.0f);
}
else if (((flags & ImGuiScrollFlags_KeepVisibleCenterY) && !fully_visible_y) || (flags & ImGuiScrollFlags_AlwaysCenterY))
{
float target_y = can_be_fully_visible_y ? ImFloor((item_rect.Min.y + item_rect.Max.y - window->InnerRect.GetHeight()) * 0.5f) : item_rect.Min.y;
SetScrollFromPosY(window, target_y - window->Pos.y, 0.0f);
}
ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
ImVec2 delta_scroll = next_scroll - window->Scroll;
// Also scroll parent window to keep us into view if necessary
if (window->Flags & ImGuiWindowFlags_ChildWindow)
delta_scroll += ScrollToBringRectIntoView(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll));
if (!(flags & ImGuiScrollFlags_NoScrollParent) && (window->Flags & ImGuiWindowFlags_ChildWindow))
{
// FIXME-SCROLL: May be an option?
if ((in_flags & (ImGuiScrollFlags_AlwaysCenterX | ImGuiScrollFlags_KeepVisibleCenterX)) != 0)
in_flags = (in_flags & ~ImGuiScrollFlags_MaskX_) | ImGuiScrollFlags_KeepVisibleEdgeX;
if ((in_flags & (ImGuiScrollFlags_AlwaysCenterY | ImGuiScrollFlags_KeepVisibleCenterY)) != 0)
in_flags = (in_flags & ~ImGuiScrollFlags_MaskY_) | ImGuiScrollFlags_KeepVisibleEdgeY;
delta_scroll += ScrollToRectEx(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll), in_flags);
}
return delta_scroll;
}
@ -9026,7 +9074,7 @@ bool ImGui::NavMoveRequestButNoResultYet()
}
// FIXME: ScoringRect is not set
void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags)
void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(g.NavWindow != NULL);
@ -9035,6 +9083,7 @@ void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavM
g.NavMoveDirForDebug = move_dir;
g.NavMoveClipDir = clip_dir;
g.NavMoveFlags = move_flags;
g.NavMoveScrollFlags = scroll_flags;
g.NavMoveForwardToNextFrame = false;
g.NavMoveKeyMods = g.IO.KeyMods;
g.NavMoveResultLocal.Clear();
@ -9051,7 +9100,7 @@ void ImGui::NavMoveRequestCancel()
}
// 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)
void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(g.NavMoveForwardToNextFrame == false);
@ -9060,6 +9109,7 @@ void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNav
g.NavMoveDir = move_dir;
g.NavMoveClipDir = clip_dir;
g.NavMoveFlags = move_flags | ImGuiNavMoveFlags_Forwarded;
g.NavMoveScrollFlags = scroll_flags;
}
// Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
@ -9422,6 +9472,7 @@ void ImGui::NavUpdateCreateMoveRequest()
// Initiate directional inputs request
g.NavMoveDir = ImGuiDir_None;
g.NavMoveFlags = ImGuiNavMoveFlags_None;
g.NavMoveScrollFlags = ImGuiScrollFlags_None;
if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs))
{
const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat;
@ -9454,7 +9505,7 @@ void ImGui::NavUpdateCreateMoveRequest()
// Submit
g.NavMoveForwardToNextFrame = false;
if (g.NavMoveDir != ImGuiDir_None)
NavMoveRequestSubmit(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags);
NavMoveRequestSubmit(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags);
// Moving with no reference triggers a init request (will be used as a fallback if the direction fails to find a match)
if (g.NavMoveSubmitted && g.NavId == 0)
@ -9534,8 +9585,9 @@ void ImGui::NavMoveRequestApplyResult()
if (g.NavLayer == ImGuiNavLayer_Main)
{
ImVec2 delta_scroll;
if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdge)
if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdgeY)
{
// FIXME: Should remove this
float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
delta_scroll.y = result->Window->Scroll.y - scroll_target;
SetScrollY(result->Window, scroll_target);
@ -9543,7 +9595,7 @@ void ImGui::NavMoveRequestApplyResult()
else
{
ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos);
delta_scroll = ScrollToBringRectIntoView(result->Window, rect_abs);
delta_scroll = ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags);
}
// Offset our result position so mouse position can be applied immediately after in NavUpdate()
@ -9670,13 +9722,13 @@ static float ImGui::NavUpdatePageUpPageDown()
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.
// Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdgeY 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.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY;
// FIXME-NAV: MoveClipDir left to _None, intentional?
}
else if (end_pressed)
@ -9685,7 +9737,7 @@ static float ImGui::NavUpdatePageUpPageDown()
if (nav_rect_rel.IsInverted())
nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
g.NavMoveDir = ImGuiDir_Up;
g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY;
// FIXME-NAV: MoveClipDir left to _None, intentional?
}
return nav_scoring_rect_offset_y;
@ -9756,7 +9808,7 @@ static void ImGui::NavEndFrame()
if (do_forward)
{
window->NavRectRel[g.NavLayer] = bb_rel;
NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags);
NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags, g.NavMoveScrollFlags);
}
}
}