mirror of
https://github.com/ocornut/imgui.git
synced 2024-11-24 05:19:02 +08:00
Nav: Record/restore preferred position on each given axis.
Tagging #6344 #6003 #2694 #1688 as it relates to scoring, however this doesn't technically fix any of them fully yet. But e.g. once we restore axial path for #2694 this commit will allow going back and forth to initial location.
This commit is contained in:
parent
39f7248d4a
commit
6656553fa4
@ -51,6 +51,10 @@ Other changes:
|
||||
- Drag, Sliders: if the format string doesn't contain any %, CTRL+Click to input text will
|
||||
use the default format specifier for the type. Allow display/input of raw value when using
|
||||
"enums" patterns (display label instead of value) + allow using when value is hidden. (#6405)
|
||||
- Nav: Record/restore preferred position on each given axis after a movement on that axis,
|
||||
then score movement on the other axis using this as a bias. This allows going up and down
|
||||
between e.g. a large header spanning horizontal space and three-ways-columns, landing
|
||||
on the same column as before.
|
||||
- Nav: Fixed navigation within tables/columns where item boundaries goes beyond columns limits,
|
||||
unclipped bounding boxes would interfere with other columns. (#2221) [@zzzyap, @ocornut]
|
||||
- Nav: Fixed CTRL+Tab into a root window with only childs with _NavFlattened flags
|
||||
|
87
imgui.cpp
87
imgui.cpp
@ -3688,6 +3688,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* ctx, const char* name) : DrawListInst(NUL
|
||||
DrawList = &DrawListInst;
|
||||
DrawList->_Data = &Ctx->DrawListSharedData;
|
||||
DrawList->_OwnerName = Name;
|
||||
NavPreferredScoringPosRel[0] = NavPreferredScoringPosRel[1] = ImVec2(FLT_MAX, FLT_MAX);
|
||||
}
|
||||
|
||||
ImGuiWindow::~ImGuiWindow()
|
||||
@ -10620,6 +10621,12 @@ void ImGui::SetNavWindow(ImGuiWindow* window)
|
||||
NavUpdateAnyRequestFlag();
|
||||
}
|
||||
|
||||
void ImGui::NavClearPreferredPosForAxis(ImGuiAxis axis)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer][axis] = FLT_MAX;
|
||||
}
|
||||
|
||||
void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
@ -10630,6 +10637,10 @@ void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id
|
||||
g.NavFocusScopeId = focus_scope_id;
|
||||
g.NavWindow->NavLastIds[nav_layer] = id;
|
||||
g.NavWindow->NavRectRel[nav_layer] = rect_rel;
|
||||
|
||||
// Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)
|
||||
NavClearPreferredPosForAxis(ImGuiAxis_X);
|
||||
NavClearPreferredPosForAxis(ImGuiAxis_Y);
|
||||
}
|
||||
|
||||
void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
|
||||
@ -10654,6 +10665,10 @@ void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
|
||||
g.NavDisableMouseHover = true;
|
||||
else
|
||||
g.NavDisableHighlight = true;
|
||||
|
||||
// Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)
|
||||
NavClearPreferredPosForAxis(ImGuiAxis_X);
|
||||
NavClearPreferredPosForAxis(ImGuiAxis_Y);
|
||||
}
|
||||
|
||||
ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
|
||||
@ -10746,16 +10761,22 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result)
|
||||
draw_list->AddText(cand.Min, IM_COL32(255, 255, 255, 255), buf);
|
||||
}
|
||||
}
|
||||
if (IsMouseHoveringRect(cand.Min, cand.Max))
|
||||
const bool debug_hovering = IsMouseHoveringRect(cand.Min, cand.Max);
|
||||
const bool debug_tty = (g.IO.KeyCtrl && IsKeyPressed(ImGuiKey_Space));
|
||||
if (debug_hovering || debug_tty)
|
||||
{
|
||||
ImFormatString(buf, IM_ARRAYSIZE(buf),
|
||||
"d-box (%7.3f,%7.3f) -> %7.3f\nd-center (%7.3f,%7.3f) -> %7.3f\nd-axial (%7.3f,%7.3f) -> %7.3f\nnav %c, quadrant %c",
|
||||
dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[g.NavMoveDir], "WENS"[quadrant]);
|
||||
ImDrawList* draw_list = GetForegroundDrawList(window);
|
||||
draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
|
||||
draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
|
||||
draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40,0,0,200));
|
||||
draw_list->AddText(cand.Max, ~0U, buf);
|
||||
dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "-WENS"[move_dir+1], "-WENS"[quadrant+1]);
|
||||
if (debug_hovering)
|
||||
{
|
||||
ImDrawList* draw_list = GetForegroundDrawList(window);
|
||||
draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255, 200, 0, 100));
|
||||
draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255, 255, 0, 200));
|
||||
draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40, 0, 0, 200));
|
||||
draw_list->AddText(cand.Max, ~0U, buf);
|
||||
}
|
||||
if (debug_tty) { IMGUI_DEBUG_LOG_NAV("id 0x%08X\n%s\n", g.LastItemData.ID, buf); }
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -11322,11 +11343,11 @@ static void ImGui::NavUpdate()
|
||||
// [DEBUG]
|
||||
g.NavScoringDebugCount = 0;
|
||||
#if IMGUI_DEBUG_NAV_RECTS
|
||||
if (g.NavWindow)
|
||||
if (ImGuiWindow* debug_window = g.NavWindow)
|
||||
{
|
||||
ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow);
|
||||
if (1) { for (int layer = 0; layer < 2; layer++) { ImRect r = WindowRectRelToAbs(g.NavWindow, g.NavWindow->NavRectRel[layer]); draw_list->AddRect(r.Min, r.Max, IM_COL32(255,200,0,255)); } } // [DEBUG]
|
||||
if (1) { ImU32 col = (!g.NavWindow->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); }
|
||||
ImDrawList* draw_list = GetForegroundDrawList(debug_window);
|
||||
int layer = g.NavLayer; /* for (int layer = 0; layer < 2; layer++)*/ { ImRect r = WindowRectRelToAbs(debug_window, debug_window->NavRectRel[layer]); draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 200, 0, 255)); }
|
||||
//if (1) { ImU32 col = (!debug_window->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); }
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -11347,6 +11368,28 @@ void ImGui::NavInitRequestApplyResult()
|
||||
NavRestoreHighlightAfterMove();
|
||||
}
|
||||
|
||||
// Bias scoring rect ahead of scoring + update preferred pos (if missing) using source position
|
||||
static void NavBiasScoringRect(ImRect& r, ImVec2& preferred_pos_rel, ImGuiDir move_dir)
|
||||
{
|
||||
// Bias initial rect
|
||||
ImGuiContext& g = *GImGui;
|
||||
const ImVec2 rel_to_abs_offset = g.NavWindow->DC.CursorStartPos;
|
||||
|
||||
// Initialize bias on departure if we don't have any. So mouse-click + arrow will record bias.
|
||||
// - We default to L/U bias, so moving down from a large source item into several columns will land on left-most column.
|
||||
// - But each successful move sets new bias on one axis, only cleared when using mouse.
|
||||
if (preferred_pos_rel.x == FLT_MAX)
|
||||
preferred_pos_rel.x = ImMin(r.Min.x + 1.0f, r.Max.x) - rel_to_abs_offset.x;
|
||||
if (preferred_pos_rel.y == FLT_MAX)
|
||||
preferred_pos_rel.y = r.GetCenter().y - rel_to_abs_offset.y;
|
||||
|
||||
// Apply general bias on the other axis
|
||||
if (move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down)
|
||||
r.Min.x = r.Max.x = preferred_pos_rel.x + rel_to_abs_offset.x;
|
||||
else
|
||||
r.Min.y = r.Max.y = preferred_pos_rel.y + rel_to_abs_offset.y;
|
||||
}
|
||||
|
||||
void ImGui::NavUpdateCreateMoveRequest()
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
@ -11453,8 +11496,8 @@ void ImGui::NavUpdateCreateMoveRequest()
|
||||
ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);
|
||||
scoring_rect = WindowRectRelToAbs(window, nav_rect_rel);
|
||||
scoring_rect.TranslateY(scoring_rect_offset_y);
|
||||
scoring_rect.Min.x = ImMin(scoring_rect.Min.x + 1.0f, scoring_rect.Max.x);
|
||||
scoring_rect.Max.x = scoring_rect.Min.x;
|
||||
if (g.NavMoveSubmitted)
|
||||
NavBiasScoringRect(scoring_rect, window->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer], g.NavMoveDir);
|
||||
IM_ASSERT(!scoring_rect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allow us to remove extraneous ImFabs() calls in NavScoreItem().
|
||||
//GetForegroundDrawList()->AddRect(scoring_rect.Min, scoring_rect.Max, IM_COL32(255,200,0,255)); // [DEBUG]
|
||||
//if (!g.NavScoringNoClipRect.IsInverted()) { GetForegroundDrawList()->AddRect(g.NavScoringNoClipRect.Min, g.NavScoringNoClipRect.Max, IM_COL32(255, 200, 0, 255)); } // [DEBUG]
|
||||
@ -11508,12 +11551,14 @@ void ImGui::NavMoveRequestApplyResult()
|
||||
result = &g.NavTabbingResultFirst;
|
||||
|
||||
// In a situation when there are no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result)
|
||||
const ImGuiAxis axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;
|
||||
if (result == NULL)
|
||||
{
|
||||
if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing)
|
||||
g.NavMoveFlags |= ImGuiNavMoveFlags_DontSetNavHighlight;
|
||||
if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_DontSetNavHighlight) == 0)
|
||||
NavRestoreHighlightAfterMove();
|
||||
NavClearPreferredPosForAxis(axis); // On a failed move, clear preferred pos for this axis.
|
||||
IMGUI_DEBUG_LOG_NAV("[nav] NavMoveSubmitted but not led to a result!\n");
|
||||
return;
|
||||
}
|
||||
@ -11558,10 +11603,19 @@ void ImGui::NavMoveRequestApplyResult()
|
||||
g.NavJustMovedToKeyMods = g.NavMoveKeyMods;
|
||||
}
|
||||
|
||||
// Focus
|
||||
// Apply new NavID/Focus
|
||||
IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
|
||||
ImVec2 preferred_scoring_pos_rel = g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer];
|
||||
SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
|
||||
|
||||
// Restore last preferred position for current axis
|
||||
// (storing in RootWindowForNav-> as the info is desirable at the beginning of a Move Request. In theory all storage should use RootWindowForNav..)
|
||||
if ((g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) == 0)
|
||||
{
|
||||
preferred_scoring_pos_rel[axis] = result->RectRel.GetCenter()[axis];
|
||||
g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer] = preferred_scoring_pos_rel;
|
||||
}
|
||||
|
||||
// Tabbing: Activates Inputable or Focus non-Inputable
|
||||
if ((g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && (result->InFlags & ImGuiItemFlags_Inputable))
|
||||
{
|
||||
@ -11775,6 +11829,8 @@ static void ImGui::NavUpdateCreateWrappingRequest()
|
||||
if (!do_forward)
|
||||
return;
|
||||
window->NavRectRel[g.NavLayer] = bb_rel;
|
||||
NavClearPreferredPosForAxis(ImGuiAxis_X);
|
||||
NavClearPreferredPosForAxis(ImGuiAxis_Y);
|
||||
NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags, g.NavMoveScrollFlags);
|
||||
}
|
||||
|
||||
@ -14285,6 +14341,9 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)
|
||||
BulletText("NavLastIds[%d]: 0x%08X at +(%.1f,%.1f)(%.1f,%.1f)", layer, window->NavLastIds[layer], r.Min.x, r.Min.y, r.Max.x, r.Max.y);
|
||||
DebugLocateItemOnHover(window->NavLastIds[layer]);
|
||||
}
|
||||
const ImVec2* pr = window->NavPreferredScoringPosRel;
|
||||
for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)
|
||||
BulletText("NavPreferredScoringPosRel[%d] = {%.1f,%.1f)", layer, (pr[layer].x == FLT_MAX ? -99999.0f : pr[layer].x), (pr[layer].y == FLT_MAX ? -99999.0f : pr[layer].y)); // Display as 99999.0f so it looks neater.
|
||||
BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
|
||||
if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); }
|
||||
if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); }
|
||||
|
2
imgui.h
2
imgui.h
@ -23,7 +23,7 @@
|
||||
// Library Version
|
||||
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM > 12345')
|
||||
#define IMGUI_VERSION "1.89.6 WIP"
|
||||
#define IMGUI_VERSION_NUM 18955
|
||||
#define IMGUI_VERSION_NUM 18956
|
||||
#define IMGUI_HAS_TABLE
|
||||
|
||||
/*
|
||||
|
@ -2366,6 +2366,7 @@ struct IMGUI_API ImGuiWindow
|
||||
ImGuiWindow* NavLastChildNavWindow; // When going to the menu bar, we remember the child window we came from. (This could probably be made implicit if we kept g.Windows sorted by last focused including child window.)
|
||||
ImGuiID NavLastIds[ImGuiNavLayer_COUNT]; // Last known NavId for this window, per layer (0/1)
|
||||
ImRect NavRectRel[ImGuiNavLayer_COUNT]; // Reference rectangle, in window relative space
|
||||
ImVec2 NavPreferredScoringPosRel[ImGuiNavLayer_COUNT]; // Preferred X/Y position updated when moving on a given axis, reset to FLT_MAX.
|
||||
ImGuiID NavRootFocusScopeId; // Focus Scope ID at the time of Begin()
|
||||
|
||||
int MemoryDrawListIdxCapacity; // Backup of last idx/vtx count, so when waking up the window we can preallocate and avoid iterative alloc/copy
|
||||
@ -2761,6 +2762,7 @@ namespace ImGui
|
||||
IMGUI_API void SetWindowHiddendAndSkipItemsForCurrentFrame(ImGuiWindow* window);
|
||||
inline ImRect WindowRectAbsToRel(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x - off.x, r.Min.y - off.y, r.Max.x - off.x, r.Max.y - off.y); }
|
||||
inline ImRect WindowRectRelToAbs(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); }
|
||||
inline ImVec2 WindowPosRelToAbs(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x + off.x, p.y + off.y); }
|
||||
|
||||
// Windows: Display Order and Focus Order
|
||||
IMGUI_API void FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags = 0);
|
||||
@ -2908,6 +2910,7 @@ namespace ImGui
|
||||
IMGUI_API void NavMoveRequestCancel();
|
||||
IMGUI_API void NavMoveRequestApplyResult();
|
||||
IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags);
|
||||
IMGUI_API void NavClearPreferredPosForAxis(ImGuiAxis axis);
|
||||
IMGUI_API void NavUpdateCurrentWindowIsScrollPushableX();
|
||||
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 SetNavWindow(ImGuiWindow* window);
|
||||
|
@ -6219,11 +6219,13 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
|
||||
if (g.NavId == id && g.NavMoveDir == ImGuiDir_Left && is_open)
|
||||
{
|
||||
toggled = true;
|
||||
NavClearPreferredPosForAxis(ImGuiAxis_X);
|
||||
NavMoveRequestCancel();
|
||||
}
|
||||
if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority?
|
||||
{
|
||||
toggled = true;
|
||||
NavClearPreferredPosForAxis(ImGuiAxis_X);
|
||||
NavMoveRequestCancel();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user