Merge branch 'master' into docking

# Conflicts:
#	imgui.cpp
This commit is contained in:
ocornut
2021-09-07 18:20:12 +02:00
10 changed files with 357 additions and 262 deletions

366
imgui.cpp
View File

@ -923,15 +923,14 @@ namespace ImGui
static void NavUpdate();
static void NavUpdateWindowing();
static void NavUpdateWindowingOverlay();
static void NavUpdateInitResult();
static void NavUpdateCancelRequest();
static void NavUpdateCreateMoveRequest();
static float NavUpdatePageUpPageDown();
static inline void NavUpdateAnyRequestFlag();
static void NavEndFrame();
static bool NavScoreItem(ImGuiNavItemData* result, ImRect cand);
static void NavApplyItemToResult(ImGuiNavItemData* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel);
static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id);
static bool NavScoreItem(ImGuiNavItemData* result);
static void NavApplyItemToResult(ImGuiNavItemData* result);
static void NavProcessItem();
static ImVec2 NavCalcPreferredRefPos();
static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window);
@ -2294,21 +2293,21 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items
return;
}
// We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect
// We create the union of the ClipRect and the scoring rect which at worst should be 1 page away from ClipRect
ImRect unclipped_rect = window->ClipRect;
if (g.NavMoveRequest)
if (g.NavMoveScoringItems)
unclipped_rect.Add(g.NavScoringRect);
if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId)
unclipped_rect.Add(ImRect(window->Pos + window->NavRectRel[0].Min, window->Pos + window->NavRectRel[0].Max));
unclipped_rect.Add(ImRect(window->Pos + window->NavRectRel[0].Min, window->Pos + window->NavRectRel[0].Max)); // Could store and use NavJustMovedToRectRel
const ImVec2 pos = window->DC.CursorPos;
int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
// 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)
if (g.NavMoveScoringItems && g.NavMoveClipDir == ImGuiDir_Up)
start--;
if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down)
if (g.NavMoveScoringItems && g.NavMoveClipDir == ImGuiDir_Down)
end++;
start = ImClamp(start, 0, items_count);
@ -3154,7 +3153,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
if (id)
{
g.ActiveIdIsAlive = id;
g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
g.ActiveIdSource = (g.NavActivateId == id || g.NavActivateInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
}
// Clear declaration of inputs claimed by the widget
@ -3361,7 +3360,8 @@ void ImGui::SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemS
// Called by ItemAdd()
// Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out.
void ImGui::ItemFocusable(ImGuiWindow* window, ImGuiID id)
// [WIP] This will eventually be refactored and moved into NavProcessItem()
void ImGui::ItemInputable(ImGuiWindow* window, ImGuiID id)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(id != 0 && id == g.LastItemData.ID);
@ -3395,7 +3395,7 @@ void ImGui::ItemFocusable(ImGuiWindow* window, ImGuiID id)
}
if (is_tab_stop && window->DC.FocusCounterTabStop == g.TabFocusRequestCurrCounterTabStop)
{
g.NavJustTabbedId = id;
g.NavJustTabbedId = id; // FIXME-NAV: aim to eventually set in NavUpdate() once we finish the refactor
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_FocusedByTabbing;
return;
}
@ -3821,13 +3821,15 @@ static void ImGui::UpdateMouseInputs()
// Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
if (IsMousePosValid(&g.IO.MousePos))
g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos);
g.IO.MousePos = g.MouseLastValidPos = ImFloor(g.IO.MousePos);
// If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
else
g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
// If mouse moved we re-enable mouse hovering in case it was disabled by gamepad/keyboard. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true.
if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
g.NavDisableMouseHover = false;
@ -7066,7 +7068,7 @@ void ImGui::FocusWindow(ImGuiWindow* window)
g.NavFocusScopeId = 0;
g.NavIdIsAlive = false;
g.NavLayer = ImGuiNavLayer_Main;
g.NavInitRequest = g.NavMoveRequest = false;
g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false;
NavUpdateAnyRequestFlag();
//IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL);
}
@ -7656,6 +7658,7 @@ void ImGui::ActivateItem(ImGuiID id)
{
ImGuiContext& g = *GImGui;
g.NavNextActivateId = id;
g.NavNextActivateFlags = ImGuiActivateFlags_None;
}
void ImGui::PushFocusScope(ImGuiID id)
@ -8124,15 +8127,17 @@ void ImGui::ItemSize(const ImRect& bb, float text_baseline_y)
// Declare item bounding box for clipping and interaction.
// Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
// declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction.
bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemAddFlags flags)
bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemFlags extra_flags)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
// Set item data
// (DisplayRect is left untouched, made valid when ImGuiItemStatusFlags_HasDisplayRect is set)
g.LastItemData.ID = id;
g.LastItemData.Rect = bb;
g.LastItemData.InFlags = g.CurrentItemFlags;
g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb;
g.LastItemData.InFlags = g.CurrentItemFlags | extra_flags;
g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None;
// Directional navigation processing
@ -8151,7 +8156,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu
if (g.NavId == id || g.NavAnyRequest)
if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
NavProcessItem();
// [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd()
#ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
@ -8175,10 +8180,10 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu
return false;
//if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
// Tab stop handling (previously was using internal ItemFocusable() api)
// FIXME-NAV: We would now want to move this above the clipping test, but this would require being able to scroll and currently this would mean an extra frame. (#4079, #343)
if (flags & ImGuiItemAddFlags_Focusable)
ItemFocusable(window, id);
// [WIP] Tab stop handling (previously was using internal FocusableItemRegister() api)
// FIXME-NAV: We would now want to move this before the clipping test, but this would require being able to scroll and currently this would mean an extra frame. (#4079, #343)
if (extra_flags & ImGuiItemFlags_Inputable)
ItemInputable(window, id);
// We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
if (IsMouseHoveringRect(bb.Min, bb.Max))
@ -8444,6 +8449,7 @@ ImVec2 ImGui::GetWindowContentRegionMax()
// Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.)
// Groups are currently a mishmash of functionalities which should perhaps be clarified and separated.
// FIXME-OPT: Could we safely early out on ->SkipItems?
void ImGui::BeginGroup()
{
ImGuiContext& g = *GImGui;
@ -9324,6 +9330,7 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
//-----------------------------------------------------------------------------
// FIXME-NAV: The existence of SetNavID vs SetFocusID properly needs to be clarified/reworked.
// In our terminology those should be interchangeable. Those two functions are merely a legacy artifact, so at minimum naming should be clarified.
void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel)
{
ImGuiContext& g = *GImGui;
@ -9354,7 +9361,7 @@ void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
window->NavLastIds[nav_layer] = id;
if (g.LastItemData.ID == id)
window->NavRectRel[nav_layer] = ImRect(g.LastItemData.Rect.Min - window->Pos, g.LastItemData.Rect.Max - window->Pos);
window->NavRectRel[nav_layer] = ImRect(g.LastItemData.NavRect.Min - window->Pos, g.LastItemData.NavRect.Max - window->Pos);
if (g.ActiveIdSource == ImGuiInputSource_Nav)
g.NavDisableMouseHover = true;
@ -9385,7 +9392,7 @@ static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect
r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y);
r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y);
}
else
else // FIXME: PageUp/PageDown are leaving move_dir == None
{
r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x);
r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x);
@ -9393,15 +9400,17 @@ static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect
}
// Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057
static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand)
static bool ImGui::NavScoreItem(ImGuiNavItemData* result)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
if (g.NavLayer != window->DC.NavLayerCurrent)
return false;
const ImRect& curr = g.NavScoringRect; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width)
g.NavScoringCount++;
// FIXME: Those are not good variables names
ImRect cand = g.LastItemData.NavRect; // Current item nav rectangle
const ImRect curr = g.NavScoringRect; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width)
g.NavScoringDebugCount++;
// When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
if (window->ParentWindow == g.NavWindow)
@ -9463,24 +9472,24 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand)
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,150));
draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
draw_list->AddText(cand.Max, ~0U, buf);
}
else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
{
if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; }
if (quadrant == g.NavMoveDir)
{
ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
ImDrawList* draw_list = GetForegroundDrawList(window);
draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
draw_list->AddText(cand.Min, IM_COL32(255, 255, 255, 255), buf);
}
}
#endif
// Is it in the quadrant we're interesting in moving to?
bool new_best = false;
if (quadrant == g.NavMoveDir)
const ImGuiDir move_dir = g.NavMoveDir;
if (quadrant == move_dir)
{
// Does it beat the current best candidate?
if (dist_box < result->DistBox)
@ -9502,7 +9511,7 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand)
// Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
// (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index),
// this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis.
if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
if (((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
new_best = true;
}
}
@ -9515,7 +9524,7 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand)
// Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match
if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
if ((g.NavMoveDir == ImGuiDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiDir_Down && day > 0.0f))
if ((move_dir == ImGuiDir_Left && dax < 0.0f) || (move_dir == ImGuiDir_Right && dax > 0.0f) || (move_dir == ImGuiDir_Up && day < 0.0f) || (move_dir == ImGuiDir_Down && day > 0.0f))
{
result->DistAxial = dist_axial;
new_best = true;
@ -9524,23 +9533,25 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand)
return new_best;
}
static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel)
static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
result->Window = window;
result->ID = id;
result->ID = g.LastItemData.ID;
result->FocusScopeId = window->DC.NavFocusScopeIdCurrent;
result->RectRel = nav_bb_rel;
result->RectRel = ImRect(g.LastItemData.NavRect.Min - window->Pos, g.LastItemData.NavRect.Max - window->Pos);
}
// We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)
static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
// This is called after LastItemData is set.
static void ImGui::NavProcessItem()
{
ImGuiContext& g = *GImGui;
//if (!g.IO.NavActive) // [2017/10/06] Removed this possibly redundant test but I am not sure of all the side-effects yet. Some of the feature here will need to work regardless of using a _NoNavInputs flag.
// return;
ImGuiWindow* window = g.CurrentWindow;
const ImGuiID id = g.LastItemData.ID;
const ImRect nav_bb = g.LastItemData.NavRect;
const ImGuiItemFlags item_flags = g.LastItemData.InFlags;
const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
// Process Init Request
if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
@ -9550,7 +9561,7 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con
if (candidate_for_nav_default_focus || g.NavInitResultId == 0)
{
g.NavInitResultId = id;
g.NavInitResultRectRel = nav_bb_rel;
g.NavInitResultRectRel = ImRect(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
}
if (candidate_for_nav_default_focus)
{
@ -9560,27 +9571,22 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con
}
// Process Move Request (scoring for navigation)
// FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav)))
// FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy)
if (g.NavMoveScoringItems)
{
ImGuiNavItemData* 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)
g.NavMoveDir = g.NavMoveDirLast;
bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest;
#else
bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb);
#endif
if (new_best)
NavApplyItemToResult(result, window, id, nav_bb_rel);
if ((g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav)))
{
ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
if (NavScoreItem(result))
NavApplyItemToResult(result);
// Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
const float VISIBLE_RATIO = 0.70f;
if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO)
if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb))
NavApplyItemToResult(&g.NavMoveResultLocalVisibleSet, window, id, nav_bb_rel);
// Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
const float VISIBLE_RATIO = 0.70f;
if ((g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO)
if (NavScoreItem(&g.NavMoveResultLocalVisible))
NavApplyItemToResult(&g.NavMoveResultLocalVisible);
}
}
// Update window-relative bounding box of navigated item
@ -9590,20 +9596,38 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con
g.NavLayer = window->DC.NavLayerCurrent;
g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
g.NavIdIsAlive = true;
window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position)
window->NavRectRel[window->DC.NavLayerCurrent] = ImRect(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); // Store item bounding box (relative to window position)
}
}
bool ImGui::NavMoveRequestButNoResultYet()
{
ImGuiContext& g = *GImGui;
return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
return g.NavMoveScoringItems && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
}
// FIXME: ScoringRect is not set
void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(g.NavWindow != NULL);
g.NavMoveSubmitted = g.NavMoveScoringItems = true;
g.NavMoveDir = move_dir;
g.NavMoveDirForDebug = move_dir;
g.NavMoveClipDir = clip_dir;
g.NavMoveFlags = move_flags;
g.NavMoveForwardToNextFrame = false;
g.NavMoveKeyMods = g.IO.KeyMods;
g.NavMoveResultLocal.Clear();
g.NavMoveResultLocalVisible.Clear();
g.NavMoveResultOther.Clear();
NavUpdateAnyRequestFlag();
}
void ImGui::NavMoveRequestCancel()
{
ImGuiContext& g = *GImGui;
g.NavMoveRequest = false;
g.NavMoveSubmitted = g.NavMoveScoringItems = false;
NavUpdateAnyRequestFlag();
}
@ -9611,12 +9635,12 @@ void ImGui::NavMoveRequestCancel()
void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(g.NavMoveRequestForwardToNextFrame == false);
IM_ASSERT(g.NavMoveForwardToNextFrame == false);
NavMoveRequestCancel();
g.NavMoveRequestForwardToNextFrame = true;
g.NavMoveForwardToNextFrame = true;
g.NavMoveDir = move_dir;
g.NavMoveClipDir = clip_dir;
g.NavMoveRequestFlags = move_flags | ImGuiNavMoveFlags_Forwarded;
g.NavMoveFlags = move_flags | ImGuiNavMoveFlags_Forwarded;
}
// Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
@ -9625,8 +9649,9 @@ void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags wra
{
ImGuiContext& g = *GImGui;
IM_ASSERT(wrap_flags != 0); // Call with _WrapX, _WrapY, _LoopX, _LoopY
if (g.NavWindow == window && g.NavMoveRequest && g.NavLayer == ImGuiNavLayer_Main)
g.NavMoveRequestFlags |= wrap_flags;
// In theory we should test for NavMoveRequestButNoResultYet() but there's no point doing it, NavEndFrame() will do the same test
if (g.NavWindow == window && g.NavMoveScoringItems && g.NavLayer == ImGuiNavLayer_Main)
g.NavMoveFlags |= wrap_flags;
}
// FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
@ -9661,20 +9686,20 @@ void ImGui::NavRestoreLayer(ImGuiNavLayer layer)
if (window->NavLastIds[layer] != 0)
{
SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]);
g.NavDisableHighlight = false;
g.NavDisableMouseHover = g.NavMousePosDirty = true;
}
else
{
g.NavLayer = layer;
NavInitWindow(window, true);
}
g.NavDisableHighlight = false;
g.NavDisableMouseHover = g.NavMousePosDirty = true;
}
static inline void ImGui::NavUpdateAnyRequestFlag()
{
ImGuiContext& g = *GImGui;
g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
g.NavAnyRequest = g.NavMoveScoringItems || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
if (g.NavAnyRequest)
IM_ASSERT(g.NavWindow != NULL);
}
@ -9720,7 +9745,7 @@ static ImVec2 ImGui::NavCalcPreferredRefPos()
// Mouse (we need a fallback in case the mouse becomes invalid after being used)
if (IsMousePosValid(&g.IO.MousePos))
return g.IO.MousePos;
return g.LastValidMousePos;
return g.MouseLastValidPos;
}
else
{
@ -9776,9 +9801,7 @@ static void ImGui::NavUpdate()
ImGuiIO& io = g.IO;
io.WantSetMousePos = false;
#if 0
if (g.NavScoringCount > 0) IMGUI_DEBUG_LOG("NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest);
#endif
//if (g.NavScoringDebugCount > 0) IMGUI_DEBUG_LOG("NavScoringDebugCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.NavScoringDebugCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest);
// Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard)
// (do it before we map Keyboard input!)
@ -9814,16 +9837,16 @@ static void ImGui::NavUpdate()
// Process navigation init request (select first/default focus)
if (g.NavInitResultId != 0)
NavUpdateInitResult();
NavInitRequestApplyResult();
g.NavInitRequest = false;
g.NavInitRequestFromMove = false;
g.NavInitResultId = 0;
g.NavJustMovedToId = 0;
// Process navigation move request
if (g.NavMoveRequest)
if (g.NavMoveSubmitted)
NavMoveRequestApplyResult();
g.NavMoveRequest = false;
g.NavMoveSubmitted = g.NavMoveScoringItems = false;
// Apply application mouse position movement, after we had a chance to process move request result.
if (g.NavMousePosDirty && g.NavIdIsAlive)
@ -9834,6 +9857,7 @@ static void ImGui::NavUpdate()
{
io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos();
io.WantSetMousePos = true;
//IMGUI_DEBUG_LOG("SetMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y);
}
g.NavMousePosDirty = false;
}
@ -9858,19 +9882,28 @@ static void ImGui::NavUpdate()
NavUpdateCancelRequest();
// Process manual activation request
g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavActivateInputId = 0;
g.NavActivateFlags = ImGuiActivateFlags_None;
if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
{
bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
bool input_down = IsNavInputDown(ImGuiNavInput_Input);
bool activate_pressed = activate_down && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
bool input_pressed = input_down && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed);
if (g.ActiveId == 0 && activate_pressed)
{
g.NavActivateId = g.NavId;
g.NavActivateFlags = ImGuiActivateFlags_PreferTweak;
}
if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && input_pressed)
{
g.NavActivateInputId = g.NavId;
g.NavActivateFlags = ImGuiActivateFlags_PreferInput;
}
if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
g.NavActivateDownId = g.NavId;
if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
g.NavActivatePressedId = g.NavId;
if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
g.NavInputId = g.NavId;
}
if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
g.NavDisableHighlight = true;
@ -9878,8 +9911,15 @@ static void ImGui::NavUpdate()
IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
// Process programmatic activation request
// FIXME-NAV: Those should eventually be queued (unlike focus they don't cancel each others)
if (g.NavNextActivateId != 0)
g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
{
if (g.NavNextActivateFlags & ImGuiActivateFlags_PreferInput)
g.NavActivateInputId = g.NavNextActivateId;
else
g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavNextActivateId;
g.NavActivateFlags = g.NavNextActivateFlags;
}
g.NavNextActivateId = 0;
// Process move requests
@ -9892,12 +9932,13 @@ static void ImGui::NavUpdate()
// *Fallback* manual-scroll with Nav directional keys when window has no navigable item
ImGuiWindow* window = g.NavWindow;
const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.
if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest) //-V560
const ImGuiDir move_dir = g.NavMoveDir;
if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll && move_dir != ImGuiDir_None)
{
if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
SetScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
SetScrollX(window, ImFloor(window->Scroll.x + ((move_dir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
if (move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down)
SetScrollY(window, ImFloor(window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
}
// *Normal* Manual scroll with NavScrollXXX keys
@ -9909,8 +9950,15 @@ static void ImGui::NavUpdate()
SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
}
// Always prioritize mouse highlight if navigation is disabled
if (!nav_keyboard_active && !nav_gamepad_active)
{
g.NavDisableHighlight = true;
g.NavDisableMouseHover = g.NavMousePosDirty = false;
}
// [DEBUG]
g.NavScoringCount = 0;
g.NavScoringDebugCount = 0;
#if IMGUI_DEBUG_NAV_RECTS
if (g.NavWindow)
{
@ -9921,7 +9969,7 @@ static void ImGui::NavUpdate()
#endif
}
static void ImGui::NavUpdateInitResult()
void ImGui::NavInitRequestApplyResult()
{
// In very rare cases g.NavWindow may be null (e.g. clearing focus after requesting an init request, which does happen when releasing Alt while clicking on void)
ImGuiContext& g = *GImGui;
@ -9932,6 +9980,7 @@ static void ImGui::NavUpdateInitResult()
// FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently.
IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name);
SetNavID(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel);
g.NavIdIsAlive = true; // Mark as alive from previous frame as we got a result
if (g.NavInitRequestFromMove)
{
g.NavDisableHighlight = false;
@ -9945,20 +9994,19 @@ void ImGui::NavUpdateCreateMoveRequest()
ImGuiIO& io = g.IO;
ImGuiWindow* window = g.NavWindow;
if (g.NavMoveRequestForwardToNextFrame)
if (g.NavMoveForwardToNextFrame && window != NULL)
{
// 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)
// (preserve most state, which were already set by the NavMoveRequestForward() function)
IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
IM_ASSERT(g.NavMoveRequestFlags & ImGuiNavMoveFlags_Forwarded);
IM_ASSERT(g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded);
IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir);
g.NavMoveRequestForwardToNextFrame = false;
}
else
{
// Initiate directional inputs request
g.NavMoveDir = ImGuiDir_None;
g.NavMoveRequestFlags = ImGuiNavMoveFlags_None;
g.NavMoveFlags = ImGuiNavMoveFlags_None;
if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs))
{
const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat;
@ -9973,24 +10021,28 @@ void ImGui::NavUpdateCreateMoveRequest()
// Update PageUp/PageDown/Home/End scroll
// FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
float nav_scoring_rect_offset_y = 0.0f;
if (nav_keyboard_active)
nav_scoring_rect_offset_y = NavUpdatePageUpPageDown();
float scoring_rect_offset_y = 0.0f;
if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active)
scoring_rect_offset_y = NavUpdatePageUpPageDown();
// If we initiate a movement request and have no current NavId, we initiate a InitDefaultRequest that will be used as a fallback if the direction fails to find a match
if (g.NavMoveDir != ImGuiDir_None)
// [DEBUG] Always send a request
#if IMGUI_DEBUG_NAV_SCORING
if (io.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
g.NavMoveDirForDebug = (ImGuiDir)((g.NavMoveDirForDebug + 1) & 3);
if (io.KeyCtrl && g.NavMoveDir == ImGuiDir_None)
{
IM_ASSERT(window != NULL);
g.NavMoveRequest = true;
g.NavMoveRequestKeyMods = io.KeyMods;
g.NavMoveDirLast = g.NavMoveDir;
g.NavMoveResultLocal.Clear();
g.NavMoveResultLocalVisibleSet.Clear();
g.NavMoveResultOther.Clear();
g.NavMoveDir = g.NavMoveDirForDebug;
g.NavMoveFlags |= ImGuiNavMoveFlags_DebugNoResult;
}
#endif
// Moving with no reference triggers a init request
if (g.NavMoveRequest && g.NavId == 0)
// Submit
g.NavMoveForwardToNextFrame = false;
if (g.NavMoveDir != ImGuiDir_None)
NavMoveRequestSubmit(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags);
// 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)
{
IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer);
g.NavInitRequest = g.NavInitRequestFromMove = true;
@ -10001,7 +10053,7 @@ void ImGui::NavUpdateCreateMoveRequest()
// When using gamepad, we project the reference nav bounding box into window visible area.
// This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad every movements are relative
// (can't focus a visible object like we can with the mouse).
if (g.NavMoveRequest && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL)
if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL)
{
ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1));
if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
@ -10015,27 +10067,35 @@ void ImGui::NavUpdateCreateMoveRequest()
}
// For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items)
g.NavScoringRect = ImRect();
if (window)
ImRect scoring_rect;
if (window != NULL)
{
ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);
g.NavScoringRect = ImRect(window->Pos + nav_rect_rel.Min, window->Pos + nav_rect_rel.Max);
g.NavScoringRect.TranslateY(nav_scoring_rect_offset_y);
g.NavScoringRect.Min.x = ImMin(g.NavScoringRect.Min.x + 1.0f, g.NavScoringRect.Max.x);
g.NavScoringRect.Max.x = g.NavScoringRect.Min.x;
IM_ASSERT(!g.NavScoringRect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem().
//GetForegroundDrawList()->AddRect(g.NavScoringRect.Min, g.NavScoringRect.Max, IM_COL32(255,200,0,255)); // [DEBUG]
scoring_rect = ImRect(window->Pos + nav_rect_rel.Min, window->Pos + nav_rect_rel.Max);
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;
IM_ASSERT(!scoring_rect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem().
//GetForegroundDrawList()->AddRect(scoring_rect.Min, scoring_rect.Max, IM_COL32(255,200,0,255)); // [DEBUG]
}
g.NavScoringRect = scoring_rect;
}
// Apply result from previous frame navigation directional move request. Always called from NavUpdate()
void ImGui::NavMoveRequestApplyResult()
{
ImGuiContext& g = *GImGui;
#if IMGUI_DEBUG_NAV_SCORING
if (g.NavMoveFlags & ImGuiNavMoveFlags_DebugNoResult) // [DEBUG] Scoring all items in NavWindow at all times
return;
#endif
if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
// Select which result to use
ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : (g.NavMoveResultOther.ID != 0) ? &g.NavMoveResultOther : NULL;
// In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result)
if (result == NULL)
{
// In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result)
if (g.NavId != 0)
{
g.NavDisableHighlight = false;
@ -10044,13 +10104,10 @@ void ImGui::NavMoveRequestApplyResult()
return;
}
// Select which result to use
ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
// PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId)
result = &g.NavMoveResultLocalVisibleSet;
if (g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
if (g.NavMoveResultLocalVisible.ID != 0 && g.NavMoveResultLocalVisible.ID != g.NavId)
result = &g.NavMoveResultLocalVisible;
// Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
@ -10062,7 +10119,7 @@ void ImGui::NavMoveRequestApplyResult()
if (g.NavLayer == ImGuiNavLayer_Main)
{
ImVec2 delta_scroll;
if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_ScrollToEdge)
if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdge)
{
float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
delta_scroll.y = result->Window->Scroll.y - scroll_target;
@ -10086,10 +10143,14 @@ void ImGui::NavMoveRequestApplyResult()
// Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
g.NavJustMovedToId = result->ID;
g.NavJustMovedToFocusScopeId = result->FocusScopeId;
g.NavJustMovedToKeyMods = g.NavMoveRequestKeyMods;
g.NavJustMovedToKeyMods = g.NavMoveKeyMods;
}
// Focus
IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
// Enable nav highlight
g.NavDisableHighlight = false;
g.NavDisableMouseHover = g.NavMousePosDirty = true;
}
@ -10141,18 +10202,18 @@ static void ImGui::NavUpdateCancelRequest()
}
// Handle PageUp/PageDown/Home/End keys
// Called from NavUpdateCreateMoveRequest() which will use our output to create a move request
// FIXME-NAV: This doesn't work properly with NavFlattened siblings as we use NavWindow rectangle for reference
// FIXME-NAV: how to get Home/End to aim at the beginning/end of a 2D grid?
static float ImGui::NavUpdatePageUpPageDown()
{
ImGuiContext& g = *GImGui;
ImGuiIO& io = g.IO;
if (g.NavMoveDir != ImGuiDir_None || g.NavWindow == NULL)
return 0.0f;
if ((g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != ImGuiNavLayer_Main)
ImGuiWindow* window = g.NavWindow;
if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != ImGuiNavLayer_Main)
return 0.0f;
ImGuiWindow* window = g.NavWindow;
const bool page_up_held = IsKeyDown(io.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp);
const bool page_down_held = IsKeyDown(io.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown);
const bool home_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home);
@ -10182,14 +10243,14 @@ static float ImGui::NavUpdatePageUpPageDown()
nav_scoring_rect_offset_y = -page_offset_y;
g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item)
g.NavMoveClipDir = ImGuiDir_Up;
g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
}
else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
{
nav_scoring_rect_offset_y = +page_offset_y;
g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item)
g.NavMoveClipDir = ImGuiDir_Down;
g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
}
else if (home_pressed)
{
@ -10200,7 +10261,8 @@ static float ImGui::NavUpdatePageUpPageDown()
if (nav_rect_rel.IsInverted())
nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
g.NavMoveDir = ImGuiDir_Down;
g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
// FIXME-NAV: MoveClipDir left to _None, intentional?
}
else if (end_pressed)
{
@ -10208,7 +10270,8 @@ 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.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
// FIXME-NAV: MoveClipDir left to _None, intentional?
}
return nav_scoring_rect_offset_y;
}
@ -10226,9 +10289,9 @@ static void ImGui::NavEndFrame()
// Perform wrap-around in menus
// FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame.
ImGuiWindow* window = g.NavWindow;
const ImGuiNavMoveFlags move_flags = g.NavMoveRequestFlags;
const ImGuiNavMoveFlags move_flags = g.NavMoveFlags;
const ImGuiNavMoveFlags wanted_flags = ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY;
if (window && NavMoveRequestButNoResultYet() && (g.NavMoveRequestFlags & wanted_flags) && (g.NavMoveRequestFlags & ImGuiNavMoveFlags_Forwarded) == 0)
if (window && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & wanted_flags) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0)
{
bool do_forward = false;
ImRect bb_rel = window->NavRectRel[g.NavLayer];
@ -10484,15 +10547,17 @@ static void ImGui::NavUpdateWindowing()
FocusWindow(new_nav_window);
new_nav_window->NavLastChildNavWindow = old_nav_window;
}
g.NavDisableHighlight = false;
g.NavDisableMouseHover = true;
// Reinitialize navigation when entering menu bar with the Alt key.
// Toggle layer
const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;
const bool preserve_layer_1_nav_id = (new_nav_window->DockNodeAsHost != NULL);
if (new_nav_layer == ImGuiNavLayer_Menu && !preserve_layer_1_nav_id)
g.NavWindow->NavLastIds[new_nav_layer] = 0;
NavRestoreLayer(new_nav_layer);
if (new_nav_layer != g.NavLayer)
{
// Reinitialize navigation when entering menu bar with the Alt key (FIXME: could be a properly of the layer?)
const bool preserve_layer_1_nav_id = (new_nav_window->DockNodeAsHost != NULL);
if (new_nav_layer == ImGuiNavLayer_Menu && !preserve_layer_1_nav_id)
g.NavWindow->NavLastIds[new_nav_layer] = 0;
NavRestoreLayer(new_nav_layer);
}
}
}
@ -10587,14 +10652,16 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
return false;
if (g.ActiveIdMouseButton != -1)
mouse_button = g.ActiveIdMouseButton;
if (g.IO.MouseDown[mouse_button] == false)
if (g.IO.MouseDown[mouse_button] == false || window->SkipItems)
return false;
g.ActiveIdAllowOverlap = false;
}
else
{
// Uncommon path: items without ID
if (g.IO.MouseDown[mouse_button] == false)
if (g.IO.MouseDown[mouse_button] == false || window->SkipItems)
return false;
if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
return false;
// If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
@ -10605,10 +10672,6 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
return false;
}
// Early out
if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
return false;
// Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
// We build a throwaway ID based on current ID stack + relative AABB of items in window.
// THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
@ -10774,7 +10837,7 @@ bool ImGui::BeginDragDropTarget()
if (!(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect))
return false;
ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
if (hovered_window == NULL || window->RootWindowDockTree != hovered_window->RootWindowDockTree)
if (hovered_window == NULL || window->RootWindowDockTree != hovered_window->RootWindowDockTree || window->SkipItems)
return false;
const ImRect& display_rect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? g.LastItemData.DisplayRect : g.LastItemData.Rect;
@ -16716,7 +16779,8 @@ void ImGui::ShowMetricsWindow(bool* p_open)
Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
Text("NavActivateId/DownId/PressedId/InputId: %08X/%08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId, g.NavActivatePressedId, g.NavActivateInputId);
Text("NavActivateFlags: %04X", g.NavActivateFlags);
Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId);
Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");