mirror of
				https://github.com/Drezil/imgui.git
				synced 2025-10-31 13:11:05 +01:00 
			
		
		
		
	Refactor: Internals: Moved Navigation functions in imgui.cpp in their own section (moved NavUpdate which would not diff properly unless empty lines were removed, hence the previous patch). (part 5) (#2036, #787)
This commit is contained in:
		
							
								
								
									
										496
									
								
								imgui.cpp
									
									
									
									
									
								
							
							
						
						
									
										496
									
								
								imgui.cpp
									
									
									
									
									
								
							| @@ -3021,254 +3021,6 @@ static void NavScrollToBringItemIntoView(ImGuiWindow* window, const ImRect& item | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void ImGui::NavUpdate() | ||||
| { | ||||
|     ImGuiContext& g = *GImGui; | ||||
|     g.IO.WantSetMousePos = false; | ||||
| #if 0 | ||||
|     if (g.NavScoringCount > 0) printf("[%05d] 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 | ||||
|     // Set input source as Gamepad when buttons are pressed before we map Keyboard (some features differs when used with Gamepad vs Keyboard) | ||||
|     bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; | ||||
|     bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; | ||||
|     if (nav_gamepad_active) | ||||
|         if (g.IO.NavInputs[ImGuiNavInput_Activate] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Input] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Cancel] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Menu] > 0.0f) | ||||
|             g.NavInputSource = ImGuiInputSource_NavGamepad; | ||||
|     // Update Keyboard->Nav inputs mapping | ||||
|     if (nav_keyboard_active) | ||||
|     { | ||||
|         #define NAV_MAP_KEY(_KEY, _NAV_INPUT) if (IsKeyDown(g.IO.KeyMap[_KEY])) { g.IO.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; } | ||||
|         NAV_MAP_KEY(ImGuiKey_Space,     ImGuiNavInput_Activate ); | ||||
|         NAV_MAP_KEY(ImGuiKey_Enter,     ImGuiNavInput_Input    ); | ||||
|         NAV_MAP_KEY(ImGuiKey_Escape,    ImGuiNavInput_Cancel   ); | ||||
|         NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ ); | ||||
|         NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_); | ||||
|         NAV_MAP_KEY(ImGuiKey_UpArrow,   ImGuiNavInput_KeyUp_   ); | ||||
|         NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ ); | ||||
|         if (g.IO.KeyCtrl)   g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f; | ||||
|         if (g.IO.KeyShift)  g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f; | ||||
|         if (g.IO.KeyAlt)    g.IO.NavInputs[ImGuiNavInput_KeyMenu_]  = 1.0f; | ||||
|         #undef NAV_MAP_KEY | ||||
|     } | ||||
|     memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration)); | ||||
|     for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++) | ||||
|         g.IO.NavInputsDownDuration[i] = (g.IO.NavInputs[i] > 0.0f) ? (g.IO.NavInputsDownDuration[i] < 0.0f ? 0.0f : g.IO.NavInputsDownDuration[i] + g.IO.DeltaTime) : -1.0f; | ||||
|     // Process navigation init request (select first/default focus) | ||||
|     if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove)) | ||||
|     { | ||||
|         // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) | ||||
|         IM_ASSERT(g.NavWindow); | ||||
|         if (g.NavInitRequestFromMove) | ||||
|             SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, g.NavInitResultRectRel); | ||||
|         else | ||||
|             SetNavID(g.NavInitResultId, g.NavLayer); | ||||
|         g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel; | ||||
|     } | ||||
|     g.NavInitRequest = false; | ||||
|     g.NavInitRequestFromMove = false; | ||||
|     g.NavInitResultId = 0; | ||||
|     g.NavJustMovedToId = 0; | ||||
|     // Process navigation move request | ||||
|     if (g.NavMoveRequest && (g.NavMoveResultLocal.ID != 0 || g.NavMoveResultOther.ID != 0)) | ||||
|         NavUpdateMoveResult(); | ||||
|     // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame | ||||
|     if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive) | ||||
|     { | ||||
|         IM_ASSERT(g.NavMoveRequest); | ||||
|         if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) | ||||
|             g.NavDisableHighlight = false; | ||||
|         g.NavMoveRequestForward = ImGuiNavForward_None; | ||||
|     } | ||||
|     // Apply application mouse position movement, after we had a chance to process move request result. | ||||
|     if (g.NavMousePosDirty && g.NavIdIsAlive) | ||||
|     { | ||||
|         // Set mouse position given our knowledge of the navigated item position from last frame | ||||
|         if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) | ||||
|         { | ||||
|             if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow) | ||||
|             { | ||||
|                 g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos(); | ||||
|                 g.IO.WantSetMousePos = true; | ||||
|             } | ||||
|         } | ||||
|         g.NavMousePosDirty = false; | ||||
|     } | ||||
|     g.NavIdIsAlive = false; | ||||
|     g.NavJustTabbedId = 0; | ||||
|     IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); | ||||
|     // Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0 | ||||
|     if (g.NavWindow) | ||||
|         NavSaveLastChildNavWindow(g.NavWindow); | ||||
|     if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0) | ||||
|         g.NavWindow->NavLastChildNavWindow = NULL; | ||||
|     // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.) | ||||
|     NavUpdateWindowing(); | ||||
|     // Set output flags for user application | ||||
|     g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); | ||||
|     g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest; | ||||
|     // Process NavCancel input (to close a popup, get back to parent, clear focus) | ||||
|     if (IsNavInputPressed(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed)) | ||||
|     { | ||||
|         if (g.ActiveId != 0) | ||||
|         { | ||||
|             ClearActiveID(); | ||||
|         } | ||||
|         else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) | ||||
|         { | ||||
|             // Exit child window | ||||
|             ImGuiWindow* child_window = g.NavWindow; | ||||
|             ImGuiWindow* parent_window = g.NavWindow->ParentWindow; | ||||
|             IM_ASSERT(child_window->ChildId != 0); | ||||
|             FocusWindow(parent_window); | ||||
|             SetNavID(child_window->ChildId, 0); | ||||
|             g.NavIdIsAlive = false; | ||||
|             if (g.NavDisableMouseHover) | ||||
|                 g.NavMousePosDirty = true; | ||||
|         } | ||||
|         else if (g.OpenPopupStack.Size > 0) | ||||
|         { | ||||
|             // Close open popup/menu | ||||
|             if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) | ||||
|                 ClosePopupToLevel(g.OpenPopupStack.Size - 1); | ||||
|         } | ||||
|         else if (g.NavLayer != 0) | ||||
|         { | ||||
|             // Leave the "menu" layer | ||||
|             NavRestoreLayer(0); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were | ||||
|             if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow))) | ||||
|                 g.NavWindow->NavLastIds[0] = 0; | ||||
|             g.NavId = 0; | ||||
|         } | ||||
|     } | ||||
|     // Process manual activation request | ||||
|     g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0; | ||||
|     if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) | ||||
|     { | ||||
|         bool activate_down = IsNavInputDown(ImGuiNavInput_Activate); | ||||
|         bool activate_pressed = activate_down && IsNavInputPressed(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed); | ||||
|         if (g.ActiveId == 0 && activate_pressed) | ||||
|             g.NavActivateId = g.NavId; | ||||
|         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) && IsNavInputPressed(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed)) | ||||
|             g.NavInputId = g.NavId; | ||||
|     } | ||||
|     if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) | ||||
|         g.NavDisableHighlight = true; | ||||
|     if (g.NavActivateId != 0) | ||||
|         IM_ASSERT(g.NavActivateDownId == g.NavActivateId); | ||||
|     g.NavMoveRequest = false; | ||||
|     // Process programmatic activation request | ||||
|     if (g.NavNextActivateId != 0) | ||||
|         g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId; | ||||
|     g.NavNextActivateId = 0; | ||||
|     // Initiate directional inputs request | ||||
|     const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags; | ||||
|     if (g.NavMoveRequestForward == ImGuiNavForward_None) | ||||
|     { | ||||
|         g.NavMoveDir = ImGuiDir_None; | ||||
|         g.NavMoveRequestFlags = 0; | ||||
|         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_Right)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadRight,ImGuiNavInput_KeyRight_,ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Right; | ||||
|             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; | ||||
|         } | ||||
|         g.NavMoveClipDir = g.NavMoveDir; | ||||
|     } | ||||
|     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) | ||||
|         // (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); | ||||
|         g.NavMoveRequestForward = ImGuiNavForward_ForwardActive; | ||||
|     } | ||||
|     // Update PageUp/PageDown scroll | ||||
|     float nav_scoring_rect_offset_y = 0.0f; | ||||
|     if (nav_keyboard_active) | ||||
|         nav_scoring_rect_offset_y = NavUpdatePageUpPageDown(allowed_dir_flags); | ||||
|     // If we initiate a movement request and have no current NavId, we initiate a InitDefautRequest that will be used as a fallback if the direction fails to find a match | ||||
|     if (g.NavMoveDir != ImGuiDir_None) | ||||
|     { | ||||
|         g.NavMoveRequest = true; | ||||
|         g.NavMoveDirLast = g.NavMoveDir; | ||||
|     } | ||||
|     if (g.NavMoveRequest && g.NavId == 0) | ||||
|     { | ||||
|         g.NavInitRequest = g.NavInitRequestFromMove = true; | ||||
|         g.NavInitResultId = 0; | ||||
|         g.NavDisableHighlight = false; | ||||
|     } | ||||
|     NavUpdateAnyRequestFlag(); | ||||
|     // Scrolling | ||||
|     if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) | ||||
|     { | ||||
|         // *Fallback* manual-scroll with Nav directional keys when window has no navigable item | ||||
|         ImGuiWindow* window = g.NavWindow; | ||||
|         const float scroll_speed = ImFloor(window->CalcFontSize() * 100 * g.IO.DeltaTime + 0.5f); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. | ||||
|         if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest) | ||||
|         { | ||||
|             if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) | ||||
|                 SetWindowScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); | ||||
|             if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) | ||||
|                 SetWindowScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); | ||||
|         } | ||||
|  | ||||
|         // *Normal* Manual scroll with NavScrollXXX keys | ||||
|         // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds. | ||||
|         ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f); | ||||
|         if (scroll_dir.x != 0.0f && window->ScrollbarX) | ||||
|         { | ||||
|             SetWindowScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed)); | ||||
|             g.NavMoveFromClampedRefRect = true; | ||||
|         } | ||||
|         if (scroll_dir.y != 0.0f) | ||||
|         { | ||||
|             SetWindowScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed)); | ||||
|             g.NavMoveFromClampedRefRect = true; | ||||
|         } | ||||
|     } | ||||
|     // Reset search results | ||||
|     g.NavMoveResultLocal.Clear(); | ||||
|     g.NavMoveResultLocalVisibleSet.Clear(); | ||||
|     g.NavMoveResultOther.Clear(); | ||||
|     // 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) | ||||
|     { | ||||
|         ImGuiWindow* window = g.NavWindow; | ||||
|         ImRect window_rect_rel(window->InnerMainRect.Min - window->Pos - ImVec2(1,1), window->InnerMainRect.Max - window->Pos + ImVec2(1,1)); | ||||
|         if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer])) | ||||
|         { | ||||
|             float pad = window->CalcFontSize() * 0.5f; | ||||
|             window_rect_rel.Expand(ImVec2(-ImMin(window_rect_rel.GetWidth(), pad), -ImMin(window_rect_rel.GetHeight(), pad))); // Terrible approximation for the intent of starting navigation from first fully visible item | ||||
|             window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel); | ||||
|             g.NavId = 0; | ||||
|         } | ||||
|         g.NavMoveFromClampedRefRect = false; | ||||
|     } | ||||
|     // 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) | ||||
|     ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0); | ||||
|     g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : ImRect(0,0,0,0); | ||||
|     g.NavScoringRectScreen.TranslateY(nav_scoring_rect_offset_y); | ||||
|     g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x); | ||||
|     g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x; | ||||
|     IM_ASSERT(!g.NavScoringRectScreen.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem(). | ||||
|     //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] | ||||
|     g.NavScoringCount = 0; | ||||
| #if IMGUI_DEBUG_NAV_RECTS | ||||
|     if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) GetOverlayDrawList(g.NavWindow)->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] | ||||
|     if (g.NavWindow) { ImU32 col = (g.NavWindow->HiddenFrames == 0) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(NULL); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); GetOverlayDrawList(g.NavWindow)->AddCircleFilled(p, 3.0f, col); GetOverlayDrawList(g.NavWindow)->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport) | ||||
| { | ||||
|     window->Viewport = viewport; | ||||
| @@ -8765,7 +8517,253 @@ void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags mov | ||||
|     } | ||||
| } | ||||
|  | ||||
| // | ||||
| static void ImGui::NavUpdate() | ||||
| { | ||||
|     ImGuiContext& g = *GImGui; | ||||
|     g.IO.WantSetMousePos = false; | ||||
| #if 0 | ||||
|     if (g.NavScoringCount > 0) printf("[%05d] 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 | ||||
|     // Set input source as Gamepad when buttons are pressed before we map Keyboard (some features differs when used with Gamepad vs Keyboard) | ||||
|     bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; | ||||
|     bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; | ||||
|     if (nav_gamepad_active) | ||||
|         if (g.IO.NavInputs[ImGuiNavInput_Activate] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Input] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Cancel] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Menu] > 0.0f) | ||||
|             g.NavInputSource = ImGuiInputSource_NavGamepad; | ||||
|     // Update Keyboard->Nav inputs mapping | ||||
|     if (nav_keyboard_active) | ||||
|     { | ||||
|         #define NAV_MAP_KEY(_KEY, _NAV_INPUT) if (IsKeyDown(g.IO.KeyMap[_KEY])) { g.IO.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; } | ||||
|         NAV_MAP_KEY(ImGuiKey_Space,     ImGuiNavInput_Activate ); | ||||
|         NAV_MAP_KEY(ImGuiKey_Enter,     ImGuiNavInput_Input    ); | ||||
|         NAV_MAP_KEY(ImGuiKey_Escape,    ImGuiNavInput_Cancel   ); | ||||
|         NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ ); | ||||
|         NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_); | ||||
|         NAV_MAP_KEY(ImGuiKey_UpArrow,   ImGuiNavInput_KeyUp_   ); | ||||
|         NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ ); | ||||
|         if (g.IO.KeyCtrl)   g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f; | ||||
|         if (g.IO.KeyShift)  g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f; | ||||
|         if (g.IO.KeyAlt)    g.IO.NavInputs[ImGuiNavInput_KeyMenu_]  = 1.0f; | ||||
|         #undef NAV_MAP_KEY | ||||
|     } | ||||
|     memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration)); | ||||
|     for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++) | ||||
|         g.IO.NavInputsDownDuration[i] = (g.IO.NavInputs[i] > 0.0f) ? (g.IO.NavInputsDownDuration[i] < 0.0f ? 0.0f : g.IO.NavInputsDownDuration[i] + g.IO.DeltaTime) : -1.0f; | ||||
|     // Process navigation init request (select first/default focus) | ||||
|     if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove)) | ||||
|     { | ||||
|         // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) | ||||
|         IM_ASSERT(g.NavWindow); | ||||
|         if (g.NavInitRequestFromMove) | ||||
|             SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, g.NavInitResultRectRel); | ||||
|         else | ||||
|             SetNavID(g.NavInitResultId, g.NavLayer); | ||||
|         g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel; | ||||
|     } | ||||
|     g.NavInitRequest = false; | ||||
|     g.NavInitRequestFromMove = false; | ||||
|     g.NavInitResultId = 0; | ||||
|     g.NavJustMovedToId = 0; | ||||
|     // Process navigation move request | ||||
|     if (g.NavMoveRequest && (g.NavMoveResultLocal.ID != 0 || g.NavMoveResultOther.ID != 0)) | ||||
|         NavUpdateMoveResult(); | ||||
|     // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame | ||||
|     if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive) | ||||
|     { | ||||
|         IM_ASSERT(g.NavMoveRequest); | ||||
|         if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) | ||||
|             g.NavDisableHighlight = false; | ||||
|         g.NavMoveRequestForward = ImGuiNavForward_None; | ||||
|     } | ||||
|     // Apply application mouse position movement, after we had a chance to process move request result. | ||||
|     if (g.NavMousePosDirty && g.NavIdIsAlive) | ||||
|     { | ||||
|         // Set mouse position given our knowledge of the navigated item position from last frame | ||||
|         if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) | ||||
|         { | ||||
|             if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow) | ||||
|             { | ||||
|                 g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos(); | ||||
|                 g.IO.WantSetMousePos = true; | ||||
|             } | ||||
|         } | ||||
|         g.NavMousePosDirty = false; | ||||
|     } | ||||
|     g.NavIdIsAlive = false; | ||||
|     g.NavJustTabbedId = 0; | ||||
|     IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); | ||||
|     // Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0 | ||||
|     if (g.NavWindow) | ||||
|         NavSaveLastChildNavWindow(g.NavWindow); | ||||
|     if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0) | ||||
|         g.NavWindow->NavLastChildNavWindow = NULL; | ||||
|     // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.) | ||||
|     NavUpdateWindowing(); | ||||
|     // Set output flags for user application | ||||
|     g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); | ||||
|     g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest; | ||||
|     // Process NavCancel input (to close a popup, get back to parent, clear focus) | ||||
|     if (IsNavInputPressed(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed)) | ||||
|     { | ||||
|         if (g.ActiveId != 0) | ||||
|         { | ||||
|             ClearActiveID(); | ||||
|         } | ||||
|         else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) | ||||
|         { | ||||
|             // Exit child window | ||||
|             ImGuiWindow* child_window = g.NavWindow; | ||||
|             ImGuiWindow* parent_window = g.NavWindow->ParentWindow; | ||||
|             IM_ASSERT(child_window->ChildId != 0); | ||||
|             FocusWindow(parent_window); | ||||
|             SetNavID(child_window->ChildId, 0); | ||||
|             g.NavIdIsAlive = false; | ||||
|             if (g.NavDisableMouseHover) | ||||
|                 g.NavMousePosDirty = true; | ||||
|         } | ||||
|         else if (g.OpenPopupStack.Size > 0) | ||||
|         { | ||||
|             // Close open popup/menu | ||||
|             if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) | ||||
|                 ClosePopupToLevel(g.OpenPopupStack.Size - 1); | ||||
|         } | ||||
|         else if (g.NavLayer != 0) | ||||
|         { | ||||
|             // Leave the "menu" layer | ||||
|             NavRestoreLayer(0); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were | ||||
|             if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow))) | ||||
|                 g.NavWindow->NavLastIds[0] = 0; | ||||
|             g.NavId = 0; | ||||
|         } | ||||
|     } | ||||
|     // Process manual activation request | ||||
|     g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0; | ||||
|     if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) | ||||
|     { | ||||
|         bool activate_down = IsNavInputDown(ImGuiNavInput_Activate); | ||||
|         bool activate_pressed = activate_down && IsNavInputPressed(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed); | ||||
|         if (g.ActiveId == 0 && activate_pressed) | ||||
|             g.NavActivateId = g.NavId; | ||||
|         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) && IsNavInputPressed(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed)) | ||||
|             g.NavInputId = g.NavId; | ||||
|     } | ||||
|     if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) | ||||
|         g.NavDisableHighlight = true; | ||||
|     if (g.NavActivateId != 0) | ||||
|         IM_ASSERT(g.NavActivateDownId == g.NavActivateId); | ||||
|     g.NavMoveRequest = false; | ||||
|     // Process programmatic activation request | ||||
|     if (g.NavNextActivateId != 0) | ||||
|         g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId; | ||||
|     g.NavNextActivateId = 0; | ||||
|     // Initiate directional inputs request | ||||
|     const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags; | ||||
|     if (g.NavMoveRequestForward == ImGuiNavForward_None) | ||||
|     { | ||||
|         g.NavMoveDir = ImGuiDir_None; | ||||
|         g.NavMoveRequestFlags = 0; | ||||
|         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_Right)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadRight,ImGuiNavInput_KeyRight_,ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Right; | ||||
|             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; | ||||
|         } | ||||
|         g.NavMoveClipDir = g.NavMoveDir; | ||||
|     } | ||||
|     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) | ||||
|         // (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); | ||||
|         g.NavMoveRequestForward = ImGuiNavForward_ForwardActive; | ||||
|     } | ||||
|     // Update PageUp/PageDown scroll | ||||
|     float nav_scoring_rect_offset_y = 0.0f; | ||||
|     if (nav_keyboard_active) | ||||
|         nav_scoring_rect_offset_y = NavUpdatePageUpPageDown(allowed_dir_flags); | ||||
|     // If we initiate a movement request and have no current NavId, we initiate a InitDefautRequest that will be used as a fallback if the direction fails to find a match | ||||
|     if (g.NavMoveDir != ImGuiDir_None) | ||||
|     { | ||||
|         g.NavMoveRequest = true; | ||||
|         g.NavMoveDirLast = g.NavMoveDir; | ||||
|     } | ||||
|     if (g.NavMoveRequest && g.NavId == 0) | ||||
|     { | ||||
|         g.NavInitRequest = g.NavInitRequestFromMove = true; | ||||
|         g.NavInitResultId = 0; | ||||
|         g.NavDisableHighlight = false; | ||||
|     } | ||||
|     NavUpdateAnyRequestFlag(); | ||||
|     // Scrolling | ||||
|     if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) | ||||
|     { | ||||
|         // *Fallback* manual-scroll with Nav directional keys when window has no navigable item | ||||
|         ImGuiWindow* window = g.NavWindow; | ||||
|         const float scroll_speed = ImFloor(window->CalcFontSize() * 100 * g.IO.DeltaTime + 0.5f); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. | ||||
|         if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest) | ||||
|         { | ||||
|             if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) | ||||
|                 SetWindowScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); | ||||
|             if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) | ||||
|                 SetWindowScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); | ||||
|         } | ||||
|  | ||||
|         // *Normal* Manual scroll with NavScrollXXX keys | ||||
|         // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds. | ||||
|         ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f); | ||||
|         if (scroll_dir.x != 0.0f && window->ScrollbarX) | ||||
|         { | ||||
|             SetWindowScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed)); | ||||
|             g.NavMoveFromClampedRefRect = true; | ||||
|         } | ||||
|         if (scroll_dir.y != 0.0f) | ||||
|         { | ||||
|             SetWindowScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed)); | ||||
|             g.NavMoveFromClampedRefRect = true; | ||||
|         } | ||||
|     } | ||||
|     // Reset search results | ||||
|     g.NavMoveResultLocal.Clear(); | ||||
|     g.NavMoveResultLocalVisibleSet.Clear(); | ||||
|     g.NavMoveResultOther.Clear(); | ||||
|     // 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) | ||||
|     { | ||||
|         ImGuiWindow* window = g.NavWindow; | ||||
|         ImRect window_rect_rel(window->InnerMainRect.Min - window->Pos - ImVec2(1,1), window->InnerMainRect.Max - window->Pos + ImVec2(1,1)); | ||||
|         if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer])) | ||||
|         { | ||||
|             float pad = window->CalcFontSize() * 0.5f; | ||||
|             window_rect_rel.Expand(ImVec2(-ImMin(window_rect_rel.GetWidth(), pad), -ImMin(window_rect_rel.GetHeight(), pad))); // Terrible approximation for the intent of starting navigation from first fully visible item | ||||
|             window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel); | ||||
|             g.NavId = 0; | ||||
|         } | ||||
|         g.NavMoveFromClampedRefRect = false; | ||||
|     } | ||||
|     // 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) | ||||
|     ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0); | ||||
|     g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : ImRect(0,0,0,0); | ||||
|     g.NavScoringRectScreen.TranslateY(nav_scoring_rect_offset_y); | ||||
|     g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x); | ||||
|     g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x; | ||||
|     IM_ASSERT(!g.NavScoringRectScreen.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem(). | ||||
|     //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] | ||||
|     g.NavScoringCount = 0; | ||||
| #if IMGUI_DEBUG_NAV_RECTS | ||||
|     if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) GetOverlayDrawList(g.NavWindow)->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] | ||||
|     if (g.NavWindow) { ImU32 col = (g.NavWindow->HiddenFrames == 0) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(NULL); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); GetOverlayDrawList(g.NavWindow)->AddCircleFilled(p, 3.0f, col); GetOverlayDrawList(g.NavWindow)->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static void ImGui::NavUpdateMoveResult() | ||||
| { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user