Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/wolfpld/tracy.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/imgui
diff options
context:
space:
mode:
authorBartosz Taudul <wolf@nereid.pl>2021-10-12 21:23:38 +0300
committerBartosz Taudul <wolf@nereid.pl>2021-10-12 21:23:38 +0300
commit2493cad5ad11a068025380e91c8a6d2ea5315ca6 (patch)
tree72fb5618ebfd86b23027c3412940dba3e30aca99 /imgui
parentbcaac7b53fe0b396074a544ea1e385b46b45db81 (diff)
Bump ImGui do 1.85 + docking.
Diffstat (limited to 'imgui')
-rw-r--r--imgui/imconfig.h2
-rw-r--r--imgui/imgui.cpp1806
-rw-r--r--imgui/imgui.h115
-rw-r--r--imgui/imgui_demo.cpp66
-rw-r--r--imgui/imgui_draw.cpp4
-rw-r--r--imgui/imgui_internal.h365
-rw-r--r--imgui/imgui_tables.cpp10
-rw-r--r--imgui/imgui_widgets.cpp274
-rw-r--r--imgui/misc/freetype/imgui_freetype.cpp1
9 files changed, 1659 insertions, 984 deletions
diff --git a/imgui/imconfig.h b/imgui/imconfig.h
index a0c86caa..7082c550 100644
--- a/imgui/imconfig.h
+++ b/imgui/imconfig.h
@@ -33,7 +33,7 @@
// It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp.
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. Not recommended.
-//#define IMGUI_DISABLE_METRICS_WINDOW // Disable metrics/debugger window: ShowMetricsWindow() will be empty.
+//#define IMGUI_DISABLE_METRICS_WINDOW // Disable metrics/debugger and other debug tools: ShowMetricsWindow() and ShowStackToolWindow() will be empty.
//---- Don't implement some functions to reduce linkage requirements.
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
diff --git a/imgui/imgui.cpp b/imgui/imgui.cpp
index bc1616e4..9e0cbae9 100644
--- a/imgui/imgui.cpp
+++ b/imgui/imgui.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.84
+// dear imgui, v1.85
// (main code and documentation)
// Help:
@@ -15,7 +15,10 @@
// - Wiki https://github.com/ocornut/imgui/wiki (lots of good stuff there)
// - Glossary https://github.com/ocornut/imgui/wiki/Glossary
// - Issues & support https://github.com/ocornut/imgui/issues
-// - Discussions https://github.com/ocornut/imgui/discussions
+
+// Getting Started?
+// - For first-time users having issues compiling/linking/running or issues loading fonts:
+// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above.
// Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
// See LICENSE.txt for copyright and licensing details (standard MIT License).
@@ -80,6 +83,7 @@ CODE
// [SECTION] DOCKING
// [SECTION] PLATFORM DEPENDENT HELPERS
// [SECTION] METRICS/DEBUGGER WINDOW
+// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, STACK TOOL)
*/
@@ -386,6 +390,7 @@ CODE
- 2021/XX/XX (1.XX) - Moved IME support functions from io.ImeSetInputScreenPosFn, io.ImeWindowHandle to the PlatformIO api.
+ - 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead for generally 'GetContentRegionAvail().x' is more useful.
- 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019):
- ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList()
- ImFont::GlyphRangesBuilder -> use ImFontGlyphRangesBuilder
@@ -919,30 +924,31 @@ namespace ImGui
static void NavUpdate();
static void NavUpdateWindowing();
static void NavUpdateWindowingOverlay();
-static void NavUpdateMoveResult();
-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);
static void NavRestoreLayer(ImGuiNavLayer layer);
static int FindWindowFocusIndex(ImGuiWindow* window);
-// Error Checking
+// Error Checking and Debug Tools
static void ErrorCheckNewFrameSanityChecks();
static void ErrorCheckEndFrameSanityChecks();
+static void UpdateDebugToolItemPicker();
+static void UpdateDebugToolStackQueries();
// Misc
static void UpdateSettings();
static void UpdateMouseInputs();
static void UpdateMouseWheel();
static void UpdateTabFocus();
-static void UpdateDebugToolItemPicker();
static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect);
static void RenderWindowOuterBorders(ImGuiWindow* window);
static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size);
@@ -952,6 +958,7 @@ static void EndFrameDrawDimmedBackgrounds();
// Viewports
const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHashStr("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter.
static ImGuiViewportP* AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& platform_pos, const ImVec2& size, ImGuiViewportFlags flags);
+static void DestroyViewport(ImGuiViewportP* viewport);
static void UpdateViewportsNewFrame();
static void UpdateViewportsEndFrame();
static void WindowSelectViewport(ImGuiWindow* window);
@@ -2289,21 +2296,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);
@@ -3008,10 +3015,9 @@ ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
ImGuiID seed = IDStack.back();
ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
ImGui::KeepAliveID(id);
-#ifdef IMGUI_ENABLE_TEST_ENGINE
ImGuiContext& g = *GImGui;
- IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
-#endif
+ if (g.DebugHookIdInfo == id)
+ ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
return id;
}
@@ -3020,10 +3026,9 @@ ImGuiID ImGuiWindow::GetID(const void* ptr)
ImGuiID seed = IDStack.back();
ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
ImGui::KeepAliveID(id);
-#ifdef IMGUI_ENABLE_TEST_ENGINE
ImGuiContext& g = *GImGui;
- IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr);
-#endif
+ if (g.DebugHookIdInfo == id)
+ ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL);
return id;
}
@@ -3032,10 +3037,9 @@ ImGuiID ImGuiWindow::GetID(int n)
ImGuiID seed = IDStack.back();
ImGuiID id = ImHashData(&n, sizeof(n), seed);
ImGui::KeepAliveID(id);
-#ifdef IMGUI_ENABLE_TEST_ENGINE
ImGuiContext& g = *GImGui;
- IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n);
-#endif
+ if (g.DebugHookIdInfo == id)
+ ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
return id;
}
@@ -3043,10 +3047,9 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
{
ImGuiID seed = IDStack.back();
ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
-#ifdef IMGUI_ENABLE_TEST_ENGINE
ImGuiContext& g = *GImGui;
- IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
-#endif
+ if (g.DebugHookIdInfo == id)
+ ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
return id;
}
@@ -3054,10 +3057,9 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
{
ImGuiID seed = IDStack.back();
ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
-#ifdef IMGUI_ENABLE_TEST_ENGINE
ImGuiContext& g = *GImGui;
- IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr);
-#endif
+ if (g.DebugHookIdInfo == id)
+ ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL);
return id;
}
@@ -3065,10 +3067,9 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n)
{
ImGuiID seed = IDStack.back();
ImGuiID id = ImHashData(&n, sizeof(n), seed);
-#ifdef IMGUI_ENABLE_TEST_ENGINE
ImGuiContext& g = *GImGui;
- IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n);
-#endif
+ if (g.DebugHookIdInfo == id)
+ ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
return id;
}
@@ -3149,7 +3150,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
@@ -3246,7 +3247,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags;
if (!(status_flags & ImGuiItemStatusFlags_HoveredRect))
return false;
- IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function
+ IM_ASSERT((flags & (ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy | ImGuiHoveredFlags_DockHierarchy)) == 0); // Flags not supported by this function
// Test if we are hovering the right window (our window could be behind another window)
// [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851)
@@ -3332,13 +3333,13 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
return true;
}
-bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
+bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
if (!bb.Overlaps(window->ClipRect))
if (id == 0 || (id != g.ActiveId && id != g.NavId))
- if (clip_even_when_logged || !g.LogEnabled)
+ if (!g.LogEnabled)
return true;
return false;
}
@@ -3356,7 +3357,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);
@@ -3364,7 +3366,6 @@ void ImGui::ItemFocusable(ImGuiWindow* window, ImGuiID id)
// Increment counters
// FIXME: ImGuiItemFlags_Disabled should disable more.
const bool is_tab_stop = (g.LastItemData.InFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0;
- window->DC.FocusCounterRegular++;
if (is_tab_stop)
{
window->DC.FocusCounterTabStop++;
@@ -3374,7 +3375,7 @@ void ImGui::ItemFocusable(ImGuiWindow* window, ImGuiID id)
// Process TAB/Shift-TAB to tab *OUT* of the currently focused item.
// (Note that we can always TAB out of a widget that doesn't allow tabbing in)
- if (g.ActiveId == id && g.TabFocusPressed && !IsActiveIdUsingKey(ImGuiKey_Tab) && g.TabFocusRequestNextWindow == NULL)
+ if (g.ActiveId == id && g.TabFocusPressed && g.TabFocusRequestNextWindow == NULL)
{
g.TabFocusRequestNextWindow = window;
g.TabFocusRequestNextCounterTabStop = window->DC.FocusCounterTabStop + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items.
@@ -3383,14 +3384,9 @@ void ImGui::ItemFocusable(ImGuiWindow* window, ImGuiID id)
// Handle focus requests
if (g.TabFocusRequestCurrWindow == window)
{
- if (window->DC.FocusCounterRegular == g.TabFocusRequestCurrCounterRegular)
- {
- g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_FocusedByCode;
- return;
- }
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;
}
@@ -3685,7 +3681,10 @@ void ImGui::UpdateMouseMovingWindowNewFrame()
KeepAliveID(g.ActiveId);
IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindowDockTree);
ImGuiWindow* moving_window = g.MovingWindow->RootWindowDockTree;
- if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
+
+ // When a window stop being submitted while being dragged, it may will its viewport until next Begin()
+ const bool window_disappared = (!moving_window->WasActive || moving_window->Viewport == NULL);
+ if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos) && !window_disappared)
{
ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
@@ -3702,17 +3701,20 @@ void ImGui::UpdateMouseMovingWindowNewFrame()
}
else
{
- // Try to merge the window back into the main viewport.
- // This works because MouseViewport should be != MovingWindow->Viewport on release (as per code in UpdateViewports)
- if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)
- UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseViewport);
+ if (!window_disappared)
+ {
+ // Try to merge the window back into the main viewport.
+ // This works because MouseViewport should be != MovingWindow->Viewport on release (as per code in UpdateViewports)
+ if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)
+ UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseViewport);
- // Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button.
- if (!IsDragDropPayloadBeingAccepted())
- g.MouseViewport = moving_window->Viewport;
+ // Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button.
+ if (!IsDragDropPayloadBeingAccepted())
+ g.MouseViewport = moving_window->Viewport;
- // Clear the NoInput window flag set by the Viewport system
- moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; // FIXME-VIEWPORT: Test engine managed to crash here because Viewport was NULL.
+ // Clear the NoInput window flag set by the Viewport system
+ moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; // FIXME-VIEWPORT: Test engine managed to crash here because Viewport was NULL.
+ }
g.MovingWindow = NULL;
ClearActiveID();
@@ -3816,13 +3818,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;
@@ -3967,7 +3971,10 @@ void ImGui::UpdateTabFocus()
ImGuiContext& g = *GImGui;
// Pressing TAB activate widget focus
- g.TabFocusPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab));
+ g.TabFocusPressed = false;
+ if (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
+ if (!g.IO.KeyCtrl && !g.IO.KeyAlt && IsKeyPressedMap(ImGuiKey_Tab) && !IsActiveIdUsingKey(ImGuiKey_Tab))
+ g.TabFocusPressed = true;
if (g.ActiveId == 0 && g.TabFocusPressed)
{
// - This path is only taken when no widget are active/tabbed-into yet.
@@ -3975,7 +3982,6 @@ void ImGui::UpdateTabFocus()
// - Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also
// manipulate the Next fields here even though they will be turned into Curr fields below.
g.TabFocusRequestNextWindow = g.NavWindow;
- g.TabFocusRequestNextCounterRegular = INT_MAX;
if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
g.TabFocusRequestNextCounterTabStop = g.NavIdTabCounter + (g.IO.KeyShift ? -1 : 0);
else
@@ -3984,17 +3990,15 @@ void ImGui::UpdateTabFocus()
// Turn queued focus request into current one
g.TabFocusRequestCurrWindow = NULL;
- g.TabFocusRequestCurrCounterRegular = g.TabFocusRequestCurrCounterTabStop = INT_MAX;
+ g.TabFocusRequestCurrCounterTabStop = INT_MAX;
if (g.TabFocusRequestNextWindow != NULL)
{
ImGuiWindow* window = g.TabFocusRequestNextWindow;
g.TabFocusRequestCurrWindow = window;
- if (g.TabFocusRequestNextCounterRegular != INT_MAX && window->DC.FocusCounterRegular != -1)
- g.TabFocusRequestCurrCounterRegular = ImModPositive(g.TabFocusRequestNextCounterRegular, window->DC.FocusCounterRegular + 1);
if (g.TabFocusRequestNextCounterTabStop != INT_MAX && window->DC.FocusCounterTabStop != -1)
g.TabFocusRequestCurrCounterTabStop = ImModPositive(g.TabFocusRequestNextCounterTabStop, window->DC.FocusCounterTabStop + 1);
g.TabFocusRequestNextWindow = NULL;
- g.TabFocusRequestNextCounterRegular = g.TabFocusRequestNextCounterTabStop = INT_MAX;
+ g.TabFocusRequestNextCounterTabStop = INT_MAX;
}
g.NavIdTabCounter = INT_MAX;
@@ -4004,6 +4008,7 @@ void ImGui::UpdateTabFocus()
void ImGui::UpdateHoveredWindowAndCaptureFlags()
{
ImGuiContext& g = *GImGui;
+ ImGuiIO& io = g.IO;
g.WindowsHoverPadding = ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_HOVER_PADDING, WINDOWS_HOVER_PADDING));
// Find the window hovered by mouse:
@@ -4016,52 +4021,65 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags()
// Modal windows prevents mouse from hovering behind them.
ImGuiWindow* modal_window = GetTopMostPopupModal();
- if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindowDockTree, modal_window))
+ if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindowDockTree, modal_window, true, true))
clear_hovered_windows = true;
// Disabled mouse?
- if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
+ if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)
clear_hovered_windows = true;
- // We track click ownership. When clicked outside of a window the click is owned by the application and won't report hovering nor request capture even while dragging over our windows afterward.
- int mouse_earliest_button_down = -1;
+ // We track click ownership. When clicked outside of a window the click is owned by the application and
+ // won't report hovering nor request capture even while dragging over our windows afterward.
+ const bool has_open_popup = (g.OpenPopupStack.Size > 0);
+ const bool has_open_modal = (modal_window != NULL);
+ int mouse_earliest_down = -1;
bool mouse_any_down = false;
- for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
+ for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
{
- if (g.IO.MouseClicked[i])
- g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (g.OpenPopupStack.Size > 0);
- mouse_any_down |= g.IO.MouseDown[i];
- if (g.IO.MouseDown[i])
- if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
- mouse_earliest_button_down = i;
+ if (io.MouseClicked[i])
+ {
+ io.MouseDownOwned[i] = (g.HoveredWindow != NULL) || has_open_popup;
+ io.MouseDownOwnedUnlessPopupClose[i] = (g.HoveredWindow != NULL) || has_open_modal;
+ }
+ mouse_any_down |= io.MouseDown[i];
+ if (io.MouseDown[i])
+ if (mouse_earliest_down == -1 || io.MouseClickedTime[i] < io.MouseClickedTime[mouse_earliest_down])
+ mouse_earliest_down = i;
}
- const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
+ const bool mouse_avail = (mouse_earliest_down == -1) || io.MouseDownOwned[mouse_earliest_down];
+ const bool mouse_avail_unless_popup_close = (mouse_earliest_down == -1) || io.MouseDownOwnedUnlessPopupClose[mouse_earliest_down];
// If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
// FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
- if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
+ if (!mouse_avail && !mouse_dragging_extern_payload)
clear_hovered_windows = true;
if (clear_hovered_windows)
g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
- // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app)
+ // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to Dear ImGui only, false = dispatch mouse to Dear ImGui + underlying app)
+ // Update io.WantCaptureMouseAllowPopupClose (experimental) to give a chance for app to react to popup closure with a drag
if (g.WantCaptureMouseNextFrame != -1)
- g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
+ {
+ io.WantCaptureMouse = io.WantCaptureMouseUnlessPopupClose = (g.WantCaptureMouseNextFrame != 0);
+ }
else
- g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.OpenPopupStack.Size > 0);
+ {
+ io.WantCaptureMouse = (mouse_avail && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_popup;
+ io.WantCaptureMouseUnlessPopupClose = (mouse_avail_unless_popup_close && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_modal;
+ }
- // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app)
+ // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to Dear ImGui only, false = dispatch keyboard info to Dear ImGui + underlying app)
if (g.WantCaptureKeyboardNextFrame != -1)
- g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
+ io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
else
- g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
- if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
- g.IO.WantCaptureKeyboard = true;
+ io.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
+ if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
+ io.WantCaptureKeyboard = true;
// Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
- g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
+ io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
}
ImGuiKeyModFlags ImGui::GetMergedKeyModFlags()
@@ -4191,6 +4209,10 @@ void ImGui::NewFrame()
g.DragDropWithinTarget = false;
g.DragDropHoldJustPressedId = 0;
+ // Close popups on focus lost (currently wip/opt-in)
+ //if (g.IO.AppFocusLost)
+ // ClosePopupsExceptModals();
+
// Clear buttons state when focus is lost
// (this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle)
if (g.IO.AppFocusLost)
@@ -4282,8 +4304,9 @@ void ImGui::NewFrame()
// Docking
DockContextNewFrameUpdateDocking(&g);
- // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
+ // [DEBUG] Update debug features
UpdateDebugToolItemPicker();
+ UpdateDebugToolStackQueries();
// Create implicit/fallback window - which we will only render it if the user has added something to it.
// We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
@@ -4296,31 +4319,6 @@ void ImGui::NewFrame()
CallContextHooks(&g, ImGuiContextHookType_NewFramePost);
}
-// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
-void ImGui::UpdateDebugToolItemPicker()
-{
- ImGuiContext& g = *GImGui;
- g.DebugItemPickerBreakId = 0;
- if (g.DebugItemPickerActive)
- {
- const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
- SetMouseCursor(ImGuiMouseCursor_Hand);
- if (IsKeyPressedMap(ImGuiKey_Escape))
- g.DebugItemPickerActive = false;
- if (IsMouseClicked(0) && hovered_id)
- {
- g.DebugItemPickerBreakId = hovered_id;
- g.DebugItemPickerActive = false;
- }
- SetNextWindowBgAlpha(0.60f);
- BeginTooltip();
- Text("HoveredId: 0x%08X", hovered_id);
- Text("Press ESC to abort picking.");
- TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!");
- EndTooltip();
- }
-}
-
void ImGui::Initialize(ImGuiContext* context)
{
ImGuiContext& g = *context;
@@ -5383,7 +5381,7 @@ void ImGui::EndChild()
ItemAdd(bb, window->ChildId);
RenderNavHighlight(bb, window->ChildId);
- // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
+ // When browsing a window that has no activable items (scroll only) we keep a highlight on the child (pass g.NavId to trick into always displaying)
if (window->DC.NavLayersActiveMask == 0 && window == g.NavWindow)
RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
}
@@ -6109,13 +6107,15 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl
void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
{
window->ParentWindow = parent_window;
- window->RootWindow = window->RootWindowDockTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
+ window->RootWindow = window->RootWindowPopupTree = window->RootWindowDockTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
{
window->RootWindowDockTree = parent_window->RootWindowDockTree;
if (!window->DockIsActive && !(parent_window->Flags & ImGuiWindowFlags_DockNodeHost))
window->RootWindow = parent_window->RootWindow;
}
+ if (parent_window && (flags & ImGuiWindowFlags_Popup))
+ window->RootWindowPopupTree = parent_window->RootWindowPopupTree;
if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
@@ -6231,12 +6231,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// Add to stack
// We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
+ g.CurrentWindow = window;
ImGuiWindowStackData window_stack_data;
window_stack_data.Window = window;
window_stack_data.ParentLastItemDataBackup = g.LastItemData;
+ window_stack_data.StackSizesOnBegin.SetToCurrentState();
g.CurrentWindowStack.push_back(window_stack_data);
- g.CurrentWindow = window;
- window->DC.StackSizesOnBegin.SetToCurrentState();
g.CurrentWindow = NULL;
if (flags & ImGuiWindowFlags_Popup)
@@ -6687,18 +6687,23 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
}
- // Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call.
+ // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71)
// When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
- // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child.
- // We also disabled this when we have dimming overlay behind this specific one child.
- // FIXME: More code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected.
+ // FIXME: User code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected (github #4493)
const bool is_undocked_or_docked_visible = !window->DockIsActive || window->DockTabIsVisible;
if (is_undocked_or_docked_visible)
{
bool render_decorations_in_parent = false;
if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
- if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0)
+ {
+ // - We test overlap with the previous child window only (testing all would end up being O(log N) not a good investment here)
+ // - We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping childs
+ ImGuiWindow* previous_child = parent_window->DC.ChildWindows.Size >= 2 ? parent_window->DC.ChildWindows[parent_window->DC.ChildWindows.Size - 2] : NULL;
+ bool previous_child_overlapping = previous_child ? previous_child->Rect().Overlaps(window->Rect()) : false;
+ bool parent_is_empty = parent_window->DrawList->VtxBuffer.Size > 0;
+ if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_is_empty && !previous_child_overlapping)
render_decorations_in_parent = true;
+ }
if (render_decorations_in_parent)
window->DrawList = parent_window->DrawList;
@@ -6779,7 +6784,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->DC.CurrentColumns = NULL;
window->DC.LayoutType = ImGuiLayoutType_Vertical;
window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
- window->DC.FocusCounterRegular = window->DC.FocusCounterTabStop = -1;
+ window->DC.FocusCounterTabStop = -1;
window->DC.ItemWidth = window->ItemWidthDefault;
window->DC.TextWrapPos = -1.0f; // disabled
@@ -6969,10 +6974,10 @@ void ImGui::End()
// Pop from window stack
g.LastItemData = g.CurrentWindowStack.back().ParentLastItemDataBackup;
- g.CurrentWindowStack.pop_back();
if (window->Flags & ImGuiWindowFlags_Popup)
g.BeginPopupStack.pop_back();
- window->DC.StackSizesOnBegin.CompareWithCurrentState();
+ g.CurrentWindowStack.back().StackSizesOnBegin.CompareWithCurrentState();
+ g.CurrentWindowStack.pop_back();
SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window);
if (g.CurrentWindow)
SetCurrentViewport(g.CurrentWindow, g.CurrentWindow->Viewport);
@@ -7042,7 +7047,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);
}
@@ -7083,8 +7088,18 @@ void ImGui::FocusWindow(ImGuiWindow* window)
void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window)
{
ImGuiContext& g = *GImGui;
-
- const int start_idx = ((under_this_window != NULL) ? FindWindowFocusIndex(under_this_window) : g.WindowsFocusOrder.Size) - 1;
+ int start_idx = g.WindowsFocusOrder.Size - 1;
+ if (under_this_window != NULL)
+ {
+ // Aim at root window behind us, if we are in a child window that's our own root (see #4640)
+ int offset = -1;
+ while (under_this_window->Flags & ImGuiWindowFlags_ChildWindow)
+ {
+ under_this_window = under_this_window->ParentWindow;
+ offset = 0;
+ }
+ start_idx = FindWindowFocusIndex(under_this_window) + offset;
+ }
for (int i = start_idx; i >= 0; i--)
{
// We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user.
@@ -7162,7 +7177,7 @@ void ImGui::PopItemFlag()
}
// BeginDisabled()/EndDisabled()
-// - Those can be nested but this cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep things disabled)
+// - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled)
// - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently.
// - Feedback welcome at https://github.com/ocornut/imgui/issues/211
// - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it.
@@ -7173,19 +7188,25 @@ void ImGui::BeginDisabled(bool disabled)
bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
g.DisabledAlphaBackup = g.Style.Alpha;
if (!was_disabled && disabled)
+ {
+ g.DisabledAlphaBackup = g.Style.Alpha;
g.Style.Alpha *= g.Style.DisabledAlpha; // PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha);
+ }
if (was_disabled || disabled)
g.CurrentItemFlags |= ImGuiItemFlags_Disabled;
g.ItemFlagsStack.push_back(g.CurrentItemFlags);
+ g.DisabledStackSize++;
}
void ImGui::EndDisabled()
{
ImGuiContext& g = *GImGui;
+ IM_ASSERT(g.DisabledStackSize > 0);
+ g.DisabledStackSize--;
bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
//PopItemFlag();
g.ItemFlagsStack.pop_back();
- g.CurrentItemFlags &= ~ImGuiItemFlags_Disabled;
+ g.CurrentItemFlags = g.ItemFlagsStack.back();
if (was_disabled && (g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0)
g.Style.Alpha = g.DisabledAlphaBackup; //PopStyleVar();
}
@@ -7225,15 +7246,27 @@ void ImGui::PopTextWrapPos()
window->DC.TextWrapPosStack.pop_back();
}
-// FIXME: We are exposing the docking hierarchy to end-user here (via IsWindowHovered, IsWindowFocused) which is unusual.
-bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
+static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy, bool dock_hierarchy)
{
- if (window->RootWindowDockTree == potential_parent)
+ window = window->RootWindow;
+ if (popup_hierarchy)
+ window = window->RootWindowPopupTree;
+ if (dock_hierarchy)
+ window = window->RootWindowDockTree;
+ return window;
+}
+
+bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy, bool dock_hierarchy)
+{
+ ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy, dock_hierarchy);
+ if (window_root == potential_parent)
return true;
while (window != NULL)
{
if (window == potential_parent)
return true;
+ if (window == window_root) // end of chain
+ return false;
window = window->ParentWindow;
}
return false;
@@ -7255,39 +7288,34 @@ bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_b
bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
{
- IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function
+ IM_ASSERT((flags & (ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenDisabled)) == 0); // Flags not supported by this function
ImGuiContext& g = *GImGui;
- if (g.HoveredWindow == NULL)
+ ImGuiWindow* ref_window = g.HoveredWindow;
+ ImGuiWindow* cur_window = g.CurrentWindow;
+ if (ref_window == NULL)
return false;
if ((flags & ImGuiHoveredFlags_AnyWindow) == 0)
{
- ImGuiWindow* window = g.CurrentWindow;
- switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
- {
- case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
- if (g.HoveredWindow->RootWindow != window->RootWindow)
- return false;
- break;
- case ImGuiHoveredFlags_RootWindow:
- if (g.HoveredWindow != window->RootWindow)
- return false;
- break;
- case ImGuiHoveredFlags_ChildWindows:
- if (!IsWindowChildOf(g.HoveredWindow, window))
- return false;
- break;
- default:
- if (g.HoveredWindow != window)
- return false;
- break;
- }
+ IM_ASSERT(cur_window); // Not inside a Begin()/End()
+ const bool popup_hierarchy = (flags & ImGuiHoveredFlags_NoPopupHierarchy) == 0;
+ const bool dock_hierarchy = (flags & ImGuiHoveredFlags_DockHierarchy) != 0;
+ if (flags & ImGuiHoveredFlags_RootWindow)
+ cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy, dock_hierarchy);
+
+ bool result;
+ if (flags & ImGuiHoveredFlags_ChildWindows)
+ result = IsWindowChildOf(ref_window, cur_window, popup_hierarchy, dock_hierarchy);
+ else
+ result = (ref_window == cur_window);
+ if (!result)
+ return false;
}
- if (!IsWindowContentHoverable(g.HoveredWindow, flags))
+ if (!IsWindowContentHoverable(ref_window, flags))
return false;
if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
- if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
+ if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId)
return false;
return true;
}
@@ -7295,22 +7323,24 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
{
ImGuiContext& g = *GImGui;
+ ImGuiWindow* ref_window = g.NavWindow;
+ ImGuiWindow* cur_window = g.CurrentWindow;
+ if (ref_window == NULL)
+ return false;
if (flags & ImGuiFocusedFlags_AnyWindow)
- return g.NavWindow != NULL;
+ return true;
- IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End()
- switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
- {
- case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
- return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
- case ImGuiFocusedFlags_RootWindow:
- return g.NavWindow == g.CurrentWindow->RootWindow;
- case ImGuiFocusedFlags_ChildWindows:
- return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
- default:
- return g.NavWindow == g.CurrentWindow;
- }
+ IM_ASSERT(cur_window); // Not inside a Begin()/End()
+ const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0;
+ const bool dock_hierarchy = (flags & ImGuiFocusedFlags_DockHierarchy) != 0;
+ if (flags & ImGuiHoveredFlags_RootWindow)
+ cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy, dock_hierarchy);
+
+ if (flags & ImGuiHoveredFlags_ChildWindows)
+ return IsWindowChildOf(ref_window, cur_window, popup_hierarchy, dock_hierarchy);
+ else
+ return (ref_window == cur_window);
}
ImGuiID ImGui::GetWindowDockID()
@@ -7629,6 +7659,7 @@ void ImGui::ActivateItem(ImGuiID id)
{
ImGuiContext& g = *GImGui;
g.NavNextActivateId = id;
+ g.NavNextActivateFlags = ImGuiActivateFlags_None;
}
void ImGui::PushFocusScope(ImGuiID id)
@@ -7650,12 +7681,16 @@ void ImGui::PopFocusScope()
void ImGui::SetKeyboardFocusHere(int offset)
{
- IM_ASSERT(offset >= -1); // -1 is allowed but not below
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
- g.TabFocusRequestNextWindow = window;
- g.TabFocusRequestNextCounterRegular = window->DC.FocusCounterRegular + 1 + offset;
- g.TabFocusRequestNextCounterTabStop = INT_MAX;
+ IM_ASSERT(offset >= -1); // -1 is allowed but not below
+ g.NavWindow = window;
+ ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
+ NavMoveRequestSubmit(ImGuiDir_None, ImGuiDir_None, ImGuiNavMoveFlags_Tabbing, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.
+ if (offset == -1)
+ NavMoveRequestResolveWithLastItem();
+ else
+ g.NavTabbingInputableRemaining = offset + 1;
}
void ImGui::SetItemDefaultFocus()
@@ -7664,15 +7699,17 @@ void ImGui::SetItemDefaultFocus()
ImGuiWindow* window = g.CurrentWindow;
if (!window->Appearing)
return;
- if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == window->DC.NavLayerCurrent)
- {
- g.NavInitRequest = false;
- g.NavInitResultId = g.LastItemData.ID;
- g.NavInitResultRectRel = ImRect(g.LastItemData.Rect.Min - window->Pos, g.LastItemData.Rect.Max - window->Pos);
- NavUpdateAnyRequestFlag();
- if (!IsItemVisible())
- SetScrollHereY();
- }
+ if (g.NavWindow != window->RootWindowForNav || (!g.NavInitRequest && g.NavInitResultId == 0) || g.NavLayer != window->DC.NavLayerCurrent)
+ return;
+
+ g.NavInitRequest = false;
+ g.NavInitResultId = g.LastItemData.ID;
+ g.NavInitResultRectRel = ImRect(g.LastItemData.Rect.Min - window->Pos, g.LastItemData.Rect.Max - window->Pos);
+ NavUpdateAnyRequestFlag();
+
+ // Scroll could be done in NavInitRequestApplyResult() via a opt-in flag (we however don't want regular init requests to scroll)
+ if (!IsItemVisible())
+ ScrollToRectEx(window, g.LastItemData.Rect, ImGuiScrollFlags_None);
}
void ImGui::SetStateStorage(ImGuiStorage* tree)
@@ -7724,6 +7761,8 @@ void ImGui::PushOverrideID(ImGuiID id)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
+ if (g.DebugHookIdInfo == id)
+ DebugHookIdInfo(id, ImGuiDataType_ID, NULL, NULL);
window->IDStack.push_back(id);
}
@@ -7733,11 +7772,10 @@ void ImGui::PushOverrideID(ImGuiID id)
ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed)
{
ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
- ImGui::KeepAliveID(id);
-#ifdef IMGUI_ENABLE_TEST_ENGINE
+ KeepAliveID(id);
ImGuiContext& g = *GImGui;
- IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
-#endif
+ if (g.DebugHookIdInfo == id)
+ DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
return id;
}
@@ -7922,53 +7960,13 @@ void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, voi
{
// PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations"
ImGuiContext& g = *GImGui;
- while (g.CurrentWindowStack.Size > 0)
+ while (g.CurrentWindowStack.Size > 0) //-V1044
{
- while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow))
- {
- if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name);
- EndTable();
- }
+ ErrorCheckEndWindowRecover(log_callback, user_data);
ImGuiWindow* window = g.CurrentWindow;
- IM_ASSERT(window != NULL);
- while (g.CurrentTabBar != NULL) //-V1044
- {
- if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name);
- EndTabBar();
- }
- while (window->DC.TreeDepth > 0)
- {
- if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name);
- TreePop();
- }
- while (g.GroupStack.Size > window->DC.StackSizesOnBegin.SizeOfGroupStack)
- {
- if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name);
- EndGroup();
- }
- while (window->IDStack.Size > 1)
- {
- if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name);
- PopID();
- }
- while (g.ColorStack.Size > window->DC.StackSizesOnBegin.SizeOfColorStack)
- {
- if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col));
- PopStyleColor();
- }
- while (g.StyleVarStack.Size > window->DC.StackSizesOnBegin.SizeOfStyleVarStack)
- {
- if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name);
- PopStyleVar();
- }
- while (g.FocusScopeStack.Size > window->DC.StackSizesOnBegin.SizeOfFocusScopeStack)
- {
- if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name);
- PopFocusScope();
- }
if (g.CurrentWindowStack.Size == 1)
{
- IM_ASSERT(g.CurrentWindow->IsFallbackWindow);
+ IM_ASSERT(window->IsFallbackWindow);
break;
}
IM_ASSERT(window == g.CurrentWindow);
@@ -7985,6 +7983,66 @@ void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, voi
}
}
+// Must be called before End()/EndChild()
+void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data)
+{
+ ImGuiContext& g = *GImGui;
+ while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow))
+ {
+ if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name);
+ EndTable();
+ }
+
+ ImGuiWindow* window = g.CurrentWindow;
+ ImGuiStackSizes* stack_sizes = &g.CurrentWindowStack.back().StackSizesOnBegin;
+ IM_ASSERT(window != NULL);
+ while (g.CurrentTabBar != NULL) //-V1044
+ {
+ if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name);
+ EndTabBar();
+ }
+ while (window->DC.TreeDepth > 0)
+ {
+ if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name);
+ TreePop();
+ }
+ while (g.GroupStack.Size > stack_sizes->SizeOfGroupStack) //-V1044
+ {
+ if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name);
+ EndGroup();
+ }
+ while (window->IDStack.Size > 1)
+ {
+ if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name);
+ PopID();
+ }
+ while (g.DisabledStackSize > stack_sizes->SizeOfDisabledStack) //-V1044
+ {
+ if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'", window->Name);
+ EndDisabled();
+ }
+ while (g.ColorStack.Size > stack_sizes->SizeOfColorStack)
+ {
+ if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col));
+ PopStyleColor();
+ }
+ while (g.ItemFlagsStack.Size > stack_sizes->SizeOfItemFlagsStack) //-V1044
+ {
+ if (log_callback) log_callback(user_data, "Recovered from missing PopItemFlag() in '%s'", window->Name);
+ PopItemFlag();
+ }
+ while (g.StyleVarStack.Size > stack_sizes->SizeOfStyleVarStack) //-V1044
+ {
+ if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name);
+ PopStyleVar();
+ }
+ while (g.FocusScopeStack.Size > stack_sizes->SizeOfFocusScopeStack) //-V1044
+ {
+ if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name);
+ PopFocusScope();
+ }
+}
+
// Save current stack sizes for later compare
void ImGuiStackSizes::SetToCurrentState()
{
@@ -7996,7 +8054,9 @@ void ImGuiStackSizes::SetToCurrentState()
SizeOfFontStack = (short)g.FontStack.Size;
SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size;
SizeOfGroupStack = (short)g.GroupStack.Size;
+ SizeOfItemFlagsStack = (short)g.ItemFlagsStack.Size;
SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size;
+ SizeOfDisabledStack = (short)g.DisabledStackSize;
}
// Compare to detect usage errors
@@ -8014,6 +8074,8 @@ void ImGuiStackSizes::CompareWithCurrentState()
// For color, style and font stacks there is an incentive to use Push/Begin/Pop/.../End patterns, so we relax our checks a little to allow them.
IM_ASSERT(SizeOfGroupStack == g.GroupStack.Size && "BeginGroup/EndGroup Mismatch!");
IM_ASSERT(SizeOfBeginPopupStack == g.BeginPopupStack.Size && "BeginPopup/EndPopup or BeginMenu/EndMenu Mismatch!");
+ IM_ASSERT(SizeOfDisabledStack == g.DisabledStackSize && "BeginDisabled/EndDisabled Mismatch!");
+ IM_ASSERT(SizeOfItemFlagsStack >= g.ItemFlagsStack.Size && "PushItemFlag/PopItemFlag Mismatch!");
IM_ASSERT(SizeOfColorStack >= g.ColorStack.Size && "PushStyleColor/PopStyleColor Mismatch!");
IM_ASSERT(SizeOfStyleVarStack >= g.StyleVarStack.Size && "PushStyleVar/PopStyleVar Mismatch!");
IM_ASSERT(SizeOfFontStack >= g.FontStack.Size && "PushFont/PopFont Mismatch!");
@@ -8048,7 +8110,6 @@ void ImGuiStackSizes::CompareWithCurrentState()
// - GetContentRegionMaxAbs() [Internal]
// - GetContentRegionAvail(),
// - GetWindowContentRegionMin(), GetWindowContentRegionMax()
-// - GetWindowContentRegionWidth()
// - BeginGroup()
// - EndGroup()
// Also see in imgui_widgets: tab bars, columns.
@@ -8098,15 +8159,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
@@ -8125,7 +8188,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
@@ -8144,15 +8207,15 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu
#endif
// Clipping test
- const bool is_clipped = IsClippedEx(bb, id, false);
+ const bool is_clipped = IsClippedEx(bb, id);
if (is_clipped)
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))
@@ -8416,14 +8479,9 @@ ImVec2 ImGui::GetWindowContentRegionMax()
return window->ContentRegionRect.Max - window->Pos;
}
-float ImGui::GetWindowContentRegionWidth()
-{
- ImGuiWindow* window = GImGui->CurrentWindow;
- return window->ContentRegionRect.GetWidth();
-}
-
// 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;
@@ -8568,32 +8626,80 @@ static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
return scroll;
}
+void ImGui::ScrollToItem(ImGuiScrollFlags flags)
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+ ScrollToRectEx(window, g.LastItemData.NavRect, flags);
+}
+
+void ImGui::ScrollToRect(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags)
+{
+ ScrollToRectEx(window, item_rect, flags);
+}
+
// Scroll to keep newly navigated item fully into view
-ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect)
+ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags)
{
ImGuiContext& g = *GImGui;
ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));
//GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
- ImVec2 delta_scroll;
- if (!window_rect.Contains(item_rect))
+ // Check that only one behavior is selected per axis
+ IM_ASSERT((flags & ImGuiScrollFlags_MaskX_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskX_));
+ IM_ASSERT((flags & ImGuiScrollFlags_MaskY_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskY_));
+
+ // Defaults
+ ImGuiScrollFlags in_flags = flags;
+ if ((flags & ImGuiScrollFlags_MaskX_) == 0 && window->ScrollbarX)
+ flags |= ImGuiScrollFlags_KeepVisibleEdgeX;
+ if ((flags & ImGuiScrollFlags_MaskY_) == 0)
+ flags |= window->Appearing ? ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeY;
+
+ const bool fully_visible_x = item_rect.Min.x >= window_rect.Min.x && item_rect.Max.x <= window_rect.Max.x;
+ const bool fully_visible_y = item_rect.Min.y >= window_rect.Min.y && item_rect.Max.y <= window_rect.Max.y;
+ const bool can_be_fully_visible_x = item_rect.GetWidth() <= window_rect.GetWidth();
+ const bool can_be_fully_visible_y = item_rect.GetHeight() <= window_rect.GetHeight();
+
+ if ((flags & ImGuiScrollFlags_KeepVisibleEdgeX) && !fully_visible_x)
{
- if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x)
- SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x - g.Style.ItemSpacing.x, 0.0f);
- else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x)
- SetScrollFromPosX(window, item_rect.Max.x - window->Pos.x + g.Style.ItemSpacing.x, 1.0f);
- if (item_rect.Min.y < window_rect.Min.y)
- SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y - g.Style.ItemSpacing.y, 0.0f);
- else if (item_rect.Max.y >= window_rect.Max.y)
- SetScrollFromPosY(window, item_rect.Max.y - window->Pos.y + g.Style.ItemSpacing.y, 1.0f);
+ if (item_rect.Min.x < window_rect.Min.x || !can_be_fully_visible_x)
+ SetScrollFromPosX(window, item_rect.Min.x - g.Style.ItemSpacing.x - window->Pos.x, 0.0f);
+ else if (item_rect.Max.x >= window_rect.Max.x)
+ SetScrollFromPosX(window, item_rect.Max.x + g.Style.ItemSpacing.x - window->Pos.x, 1.0f);
+ }
+ else if (((flags & ImGuiScrollFlags_KeepVisibleCenterX) && !fully_visible_x) || (flags & ImGuiScrollFlags_AlwaysCenterX))
+ {
+ float target_x = can_be_fully_visible_x ? ImFloor((item_rect.Min.x + item_rect.Max.x - window->InnerRect.GetWidth()) * 0.5f) : item_rect.Min.x;
+ SetScrollFromPosX(window, target_x - window->Pos.x, 0.0f);
+ }
- ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
- delta_scroll = next_scroll - window->Scroll;
+ if ((flags & ImGuiScrollFlags_KeepVisibleEdgeY) && !fully_visible_y)
+ {
+ if (item_rect.Min.y < window_rect.Min.y || !can_be_fully_visible_y)
+ SetScrollFromPosY(window, item_rect.Min.y - g.Style.ItemSpacing.y - window->Pos.y, 0.0f);
+ else if (item_rect.Max.y >= window_rect.Max.y)
+ SetScrollFromPosY(window, item_rect.Max.y + g.Style.ItemSpacing.y - window->Pos.y, 1.0f);
+ }
+ else if (((flags & ImGuiScrollFlags_KeepVisibleCenterY) && !fully_visible_y) || (flags & ImGuiScrollFlags_AlwaysCenterY))
+ {
+ float target_y = can_be_fully_visible_y ? ImFloor((item_rect.Min.y + item_rect.Max.y - window->InnerRect.GetHeight()) * 0.5f) : item_rect.Min.y;
+ SetScrollFromPosY(window, target_y - window->Pos.y, 0.0f);
}
+ ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
+ ImVec2 delta_scroll = next_scroll - window->Scroll;
+
// Also scroll parent window to keep us into view if necessary
- if (window->Flags & ImGuiWindowFlags_ChildWindow)
- delta_scroll += ScrollToBringRectIntoView(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll));
+ if (!(flags & ImGuiScrollFlags_NoScrollParent) && (window->Flags & ImGuiWindowFlags_ChildWindow))
+ {
+ // FIXME-SCROLL: May be an option?
+ if ((in_flags & (ImGuiScrollFlags_AlwaysCenterX | ImGuiScrollFlags_KeepVisibleCenterX)) != 0)
+ in_flags = (in_flags & ~ImGuiScrollFlags_MaskX_) | ImGuiScrollFlags_KeepVisibleEdgeX;
+ if ((in_flags & (ImGuiScrollFlags_AlwaysCenterY | ImGuiScrollFlags_KeepVisibleCenterY)) != 0)
+ in_flags = (in_flags & ~ImGuiScrollFlags_MaskY_) | ImGuiScrollFlags_KeepVisibleEdgeY;
+ delta_scroll += ScrollToRectEx(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll), in_flags);
+ }
return delta_scroll;
}
@@ -8939,6 +9045,21 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to
}
}
+void ImGui::ClosePopupsExceptModals()
+{
+ ImGuiContext& g = *GImGui;
+
+ int popup_count_to_keep;
+ for (popup_count_to_keep = g.OpenPopupStack.Size; popup_count_to_keep > 0; popup_count_to_keep--)
+ {
+ ImGuiWindow* window = g.OpenPopupStack[popup_count_to_keep - 1].Window;
+ if (!window || window->Flags & ImGuiWindowFlags_Modal)
+ break;
+ }
+ if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below
+ ClosePopupToLevel(popup_count_to_keep, true);
+}
+
void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
{
ImGuiContext& g = *GImGui;
@@ -9074,7 +9195,7 @@ void ImGui::EndPopup()
IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls
IM_ASSERT(g.BeginPopupStack.Size > 0);
- // Make all menus and popups wrap around for now, may need to expose that policy.
+ // Make all menus and popups wrap around for now, may need to expose that policy (e.g. focus scope could include wrap/loop policy flags used by new move requests)
if (g.NavWindow == window)
NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
@@ -9304,6 +9425,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;
@@ -9334,7 +9456,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;
@@ -9365,7 +9487,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);
@@ -9373,15 +9495,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)
@@ -9443,24 +9567,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)
@@ -9482,7 +9606,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;
}
}
@@ -9495,7 +9619,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;
@@ -9504,23 +9628,26 @@ 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->InFlags = g.LastItemData.InFlags;
+ 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)
@@ -9530,7 +9657,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)
{
@@ -9540,27 +9667,34 @@ 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 (item_flags & ImGuiItemFlags_Inputable)
+ g.NavTabbingInputableRemaining--;
- // 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);
+ if ((g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav)))
+ {
+ ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
+
+ if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing)
+ {
+ if (g.NavTabbingInputableRemaining == 0)
+ NavMoveRequestResolveWithLastItem();
+ }
+ else
+ {
+ 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.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
@@ -9570,43 +9704,77 @@ 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, ImGuiScrollFlags scroll_flags)
+{
+ ImGuiContext& g = *GImGui;
+ IM_ASSERT(g.NavWindow != NULL);
+
+ if (move_flags & ImGuiNavMoveFlags_Tabbing)
+ move_flags |= ImGuiNavMoveFlags_AllowCurrentNavId;
+
+ g.NavMoveSubmitted = g.NavMoveScoringItems = true;
+ g.NavMoveDir = move_dir;
+ g.NavMoveDirForDebug = move_dir;
+ g.NavMoveClipDir = clip_dir;
+ g.NavMoveFlags = move_flags;
+ g.NavMoveScrollFlags = scroll_flags;
+ g.NavMoveForwardToNextFrame = false;
+ g.NavMoveKeyMods = g.IO.KeyMods;
+ g.NavTabbingInputableRemaining = 0;
+ g.NavMoveResultLocal.Clear();
+ g.NavMoveResultLocalVisible.Clear();
+ g.NavMoveResultOther.Clear();
+ NavUpdateAnyRequestFlag();
+}
+
+void ImGui::NavMoveRequestResolveWithLastItem()
+{
+ ImGuiContext& g = *GImGui;
+ g.NavMoveScoringItems = false; // Ensure request doesn't need more processing
+ NavApplyItemToResult(&g.NavMoveResultLocal);
+ NavUpdateAnyRequestFlag();
}
void ImGui::NavMoveRequestCancel()
{
ImGuiContext& g = *GImGui;
- g.NavMoveRequest = false;
+ g.NavMoveSubmitted = g.NavMoveScoringItems = false;
NavUpdateAnyRequestFlag();
}
-void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags)
+// Forward will reuse the move request again on the next frame (generally with modifications done to it)
+void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags)
{
ImGuiContext& g = *GImGui;
- IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None);
+ IM_ASSERT(g.NavMoveForwardToNextFrame == false);
NavMoveRequestCancel();
+ g.NavMoveForwardToNextFrame = true;
g.NavMoveDir = move_dir;
g.NavMoveClipDir = clip_dir;
- g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
- g.NavMoveRequestFlags = move_flags;
- g.NavWindow->NavRectRel[g.NavLayer] = bb_rel;
+ g.NavMoveFlags = move_flags | ImGuiNavMoveFlags_Forwarded;
+ g.NavMoveScrollFlags = scroll_flags;
}
-void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
+// Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
+// popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.
+void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags wrap_flags)
{
ImGuiContext& g = *GImGui;
-
- // Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
- // popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.
- g.NavWrapRequestWindow = window;
- g.NavWrapRequestFlags = move_flags;
+ IM_ASSERT(wrap_flags != 0); // Call with _WrapX, _WrapY, _LoopX, _LoopY
+ // 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).
@@ -9641,20 +9809,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);
}
@@ -9700,7 +9868,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
{
@@ -9756,16 +9924,12 @@ static void ImGui::NavUpdate()
ImGuiIO& io = g.IO;
io.WantSetMousePos = false;
- g.NavWrapRequestWindow = NULL;
- g.NavWrapRequestFlags = ImGuiNavMoveFlags_None;
-#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!)
- bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
- bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
+ const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
+ const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
if (nav_gamepad_active && g.NavInputSource != ImGuiInputSource_Gamepad)
{
if (io.NavInputs[ImGuiNavInput_Activate] > 0.0f || io.NavInputs[ImGuiNavInput_Input] > 0.0f || io.NavInputs[ImGuiNavInput_Cancel] > 0.0f || io.NavInputs[ImGuiNavInput_Menu] > 0.0f
@@ -9796,24 +9960,17 @@ 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)
- 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;
- }
+ if (g.NavMoveSubmitted)
+ NavMoveRequestApplyResult();
+ g.NavTabbingInputableRemaining = 0;
+ 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)
@@ -9824,14 +9981,15 @@ 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;
}
g.NavIdIsAlive = false;
g.NavJustTabbedId = 0;
- IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
+ IM_ASSERT(g.NavLayer == ImGuiNavLayer_Main || g.NavLayer == ImGuiNavLayer_Menu);
- // 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
+ // Store our return window (for returning from Menu Layer to Main Layer) and clear it as soon as we step back in our own Layer 0
if (g.NavWindow)
NavSaveLastChildNavWindowIntoParent(g.NavWindow);
if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main)
@@ -9845,116 +10003,51 @@ static void ImGui::NavUpdate()
io.NavVisible = (io.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL);
// Process NavCancel input (to close a popup, get back to parent, clear focus)
- if (IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
- {
- IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n");
- if (g.ActiveId != 0)
- {
- if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel))
- ClearActiveID();
- }
- else if (g.NavLayer != ImGuiNavLayer_Main)
- {
- // Leave the "menu" layer
- NavRestoreLayer(ImGuiNavLayer_Main);
- }
- else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(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);
- ImRect child_rect = child_window->Rect();
- FocusWindow(parent_window);
- SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, ImRect(child_rect.Min - parent_window->Pos, child_rect.Max - parent_window->Pos));
- }
- else if (g.OpenPopupStack.Size > 0)
- {
- // Close open popup/menu
- if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
- ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
- }
- 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 = g.NavFocusScopeId = 0;
- }
- }
+ 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;
if (g.NavActivateId != 0)
IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
- g.NavMoveRequest = false;
// 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;
- g.NavNextActivateId = 0;
-
- // Initiate directional inputs request
- if (g.NavMoveRequestForward == ImGuiNavForward_None)
{
- g.NavMoveDir = ImGuiDir_None;
- g.NavMoveRequestFlags = ImGuiNavMoveFlags_None;
- if (g.NavWindow && !g.NavWindowingTarget && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
- {
- const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat;
- if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsNavInputTest(ImGuiNavInput_DpadLeft, read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_, read_mode))) { g.NavMoveDir = ImGuiDir_Left; }
- if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; }
- if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && (IsNavInputTest(ImGuiNavInput_DpadUp, read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_, read_mode))) { g.NavMoveDir = ImGuiDir_Up; }
- if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { 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);
- IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir);
- g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
+ 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;
- // Update PageUp/PageDown/Home/End scroll
- // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
- float nav_scoring_rect_offset_y = 0.0f;
- if (nav_keyboard_active)
- nav_scoring_rect_offset_y = NavUpdatePageUpPageDown();
-
- // 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.NavMoveRequestKeyMods = io.KeyMods;
- g.NavMoveDirLast = g.NavMoveDir;
- }
- if (g.NavMoveRequest && 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;
- // Reassigning with same value, we're being explicit here.
- g.NavInitResultId = 0; // -V1048
- g.NavDisableHighlight = false;
- }
+ // Process move requests
+ NavUpdateCreateMoveRequest();
NavUpdateAnyRequestFlag();
// Scrolling
@@ -9963,12 +10056,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)
+ 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
@@ -9980,37 +10074,15 @@ static void ImGui::NavUpdate()
SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
}
- // Reset search results
- g.NavMoveResultLocal.Clear();
- g.NavMoveResultLocalVisibleSet.Clear();
- g.NavMoveResultOther.Clear();
-
- // 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)
+ // Always prioritize mouse highlight if navigation is disabled
+ if (!nav_keyboard_active && !nav_gamepad_active)
{
- ImGuiWindow* window = g.NavWindow;
- 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]))
- {
- IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel\n");
- 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].ClipWithFull(window_rect_rel);
- g.NavId = g.NavFocusScopeId = 0;
- }
+ g.NavDisableHighlight = true;
+ g.NavDisableMouseHover = g.NavMousePosDirty = 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.NavScoringRect = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : ImRect(0, 0, 0, 0);
- 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.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
- g.NavScoringCount = 0;
+ // [DEBUG]
+ g.NavScoringDebugCount = 0;
#if IMGUI_DEBUG_NAV_RECTS
if (g.NavWindow)
{
@@ -10021,7 +10093,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;
@@ -10032,6 +10104,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;
@@ -10039,14 +10112,118 @@ static void ImGui::NavUpdateInitResult()
}
}
-// Apply result from previous frame navigation directional move request
-static void ImGui::NavUpdateMoveResult()
+void ImGui::NavUpdateCreateMoveRequest()
{
ImGuiContext& g = *GImGui;
- if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
+ ImGuiIO& io = g.IO;
+ ImGuiWindow* window = g.NavWindow;
+
+ 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.NavMoveFlags & ImGuiNavMoveFlags_Forwarded);
+ IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir);
+ }
+ else
+ {
+ // Initiate directional inputs request
+ g.NavMoveDir = ImGuiDir_None;
+ g.NavMoveFlags = ImGuiNavMoveFlags_None;
+ g.NavMoveScrollFlags = ImGuiScrollFlags_None;
+ if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs))
+ {
+ const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat;
+ if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsNavInputTest(ImGuiNavInput_DpadLeft, read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_, read_mode))) { g.NavMoveDir = ImGuiDir_Left; }
+ if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; }
+ if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && (IsNavInputTest(ImGuiNavInput_DpadUp, read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_, read_mode))) { g.NavMoveDir = ImGuiDir_Up; }
+ if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; }
+ }
+ g.NavMoveClipDir = g.NavMoveDir;
+ }
+
+ // 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 scoring_rect_offset_y = 0.0f;
+ if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active)
+ scoring_rect_offset_y = NavUpdatePageUpPageDown();
+
+ // [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)
+ {
+ g.NavMoveDir = g.NavMoveDirForDebug;
+ g.NavMoveFlags |= ImGuiNavMoveFlags_DebugNoResult;
+ }
+#endif
+
+ // Submit
+ g.NavMoveForwardToNextFrame = false;
+ if (g.NavMoveDir != ImGuiDir_None)
+ NavMoveRequestSubmit(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags);
+
+ // Moving with no reference triggers a init request (will be used as a fallback if the direction fails to find a match)
+ if (g.NavMoveSubmitted && g.NavId == 0)
+ {
+ IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer);
+ g.NavInitRequest = g.NavInitRequestFromMove = true;
+ g.NavInitResultId = 0;
+ g.NavDisableHighlight = false;
+ }
+
+ // 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.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != 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)
+ 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]))
+ {
+ IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel\n");
+ 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].ClipWithFull(window_rect_rel);
+ g.NavId = g.NavFocusScopeId = 0;
+ }
+ }
+
+ // 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 scoring_rect;
+ if (window != NULL)
+ {
+ ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);
+ 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
+
+ // 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)
+ {
+ if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing)
+ g.NavMoveFlags |= ImGuiNavMoveFlags_DontSetNavHighlight;
+ if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_DontSetNavHighlight) == 0)
{
g.NavDisableHighlight = false;
g.NavDisableMouseHover = true;
@@ -10054,13 +10231,10 @@ static void ImGui::NavUpdateMoveResult()
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)
@@ -10072,8 +10246,9 @@ static void ImGui::NavUpdateMoveResult()
if (g.NavLayer == ImGuiNavLayer_Main)
{
ImVec2 delta_scroll;
- if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_ScrollToEdge)
+ if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdgeY)
{
+ // FIXME: Should remove this
float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
delta_scroll.y = result->Window->Scroll.y - scroll_target;
SetScrollY(result->Window, scroll_target);
@@ -10081,7 +10256,7 @@ static void ImGui::NavUpdateMoveResult()
else
{
ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos);
- delta_scroll = ScrollToBringRectIntoView(result->Window, rect_abs);
+ delta_scroll = ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags);
}
// Offset our result position so mouse position can be applied immediately after in NavUpdate()
@@ -10096,84 +10271,155 @@ static void ImGui::NavUpdateMoveResult()
// 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);
- g.NavDisableHighlight = false;
- g.NavDisableMouseHover = g.NavMousePosDirty = true;
+
+ // Tabbing: Activates Inputable or Focus non-Inputable
+ if ((g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && (result->InFlags & ImGuiItemFlags_Inputable))
+ {
+ g.NavNextActivateId = result->ID;
+ g.NavNextActivateFlags = ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState;
+ g.NavMoveFlags |= ImGuiNavMoveFlags_DontSetNavHighlight;
+ }
+
+ // Activate
+ if (g.NavMoveFlags & ImGuiNavMoveFlags_Activate)
+ {
+ g.NavNextActivateId = result->ID;
+ g.NavNextActivateFlags = ImGuiActivateFlags_None;
+ }
+
+ // Enable nav highlight
+ if ((g.NavMoveFlags & ImGuiNavMoveFlags_DontSetNavHighlight) == 0)
+ {
+ g.NavDisableHighlight = false;
+ g.NavDisableMouseHover = g.NavMousePosDirty = true;
+ }
+}
+
+// Process NavCancel input (to close a popup, get back to parent, clear focus)
+// FIXME: In order to support e.g. Escape to clear a selection we'll need:
+// - either to store the equivalent of ActiveIdUsingKeyInputMask for a FocusScope and test for it.
+// - either to move most/all of those tests to the epilogue/end functions of the scope they are dealing with (e.g. exit child window in EndChild()) or in EndFrame(), to allow an earlier intercept
+static void ImGui::NavUpdateCancelRequest()
+{
+ ImGuiContext& g = *GImGui;
+ if (!IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
+ return;
+
+ IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n");
+ if (g.ActiveId != 0)
+ {
+ if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel))
+ ClearActiveID();
+ }
+ else if (g.NavLayer != ImGuiNavLayer_Main)
+ {
+ // Leave the "menu" layer
+ NavRestoreLayer(ImGuiNavLayer_Main);
+ }
+ else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(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);
+ ImRect child_rect = child_window->Rect();
+ FocusWindow(parent_window);
+ SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, ImRect(child_rect.Min - parent_window->Pos, child_rect.Max - parent_window->Pos));
+ }
+ else if (g.OpenPopupStack.Size > 0)
+ {
+ // Close open popup/menu
+ if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
+ ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
+ }
+ 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 = g.NavFocusScopeId = 0;
+ }
}
// 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);
const bool end_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End);
- if (page_up_held != page_down_held || home_pressed != end_pressed) // If either (not both) are pressed
+ if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out
+ return 0.0f;
+
+ if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll)
{
- if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll)
+ // Fallback manual-scroll when window has no navigable item
+ if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
+ SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
+ else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
+ SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
+ else if (home_pressed)
+ SetScrollY(window, 0.0f);
+ else if (end_pressed)
+ SetScrollY(window, window->ScrollMax.y);
+ }
+ else
+ {
+ ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
+ const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
+ float nav_scoring_rect_offset_y = 0.0f;
+ if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
{
- // Fallback manual-scroll when window has no navigable item
- if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
- SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
- else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
- SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
- else if (home_pressed)
- SetScrollY(window, 0.0f);
- else if (end_pressed)
- SetScrollY(window, window->ScrollMax.y);
+ 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.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
}
- else
+ else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
{
- ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
- const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
- float nav_scoring_rect_offset_y = 0.0f;
- if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
- {
- 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;
- }
- 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;
- }
- else if (home_pressed)
- {
- // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
- // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result.
- // Preserve current horizontal position if we have any.
- nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y;
- if (nav_rect_rel.IsInverted())
- nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
- g.NavMoveDir = ImGuiDir_Down;
- g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
- }
- else if (end_pressed)
- {
- nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y;
- 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;
- }
- return nav_scoring_rect_offset_y;
+ 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.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
+ }
+ else if (home_pressed)
+ {
+ // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
+ // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdgeY flag, we don't scroll immediately to avoid scrolling happening before nav result.
+ // Preserve current horizontal position if we have any.
+ nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y;
+ if (nav_rect_rel.IsInverted())
+ nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
+ g.NavMoveDir = ImGuiDir_Down;
+ g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY;
+ // FIXME-NAV: MoveClipDir left to _None, intentional?
}
+ else if (end_pressed)
+ {
+ nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y;
+ if (nav_rect_rel.IsInverted())
+ nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
+ g.NavMoveDir = ImGuiDir_Up;
+ g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY;
+ // FIXME-NAV: MoveClipDir left to _None, intentional?
+ }
+ return nav_scoring_rect_offset_y;
}
return 0.0f;
}
@@ -10187,13 +10433,14 @@ static void ImGui::NavEndFrame()
NavUpdateWindowingOverlay();
// Perform wrap-around in menus
- ImGuiWindow* window = g.NavWrapRequestWindow;
- ImGuiNavMoveFlags move_flags = g.NavWrapRequestFlags;
- if (window != NULL && g.NavWindow == window && NavMoveRequestButNoResultYet() && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == ImGuiNavLayer_Main)
+ // 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.NavMoveFlags;
+ const ImGuiNavMoveFlags wanted_flags = ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY;
+ if (window && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & wanted_flags) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0)
{
- IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
- ImRect bb_rel = window->NavRectRel[0];
-
+ bool do_forward = false;
+ ImRect bb_rel = window->NavRectRel[g.NavLayer];
ImGuiDir clip_dir = g.NavMoveDir;
if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
{
@@ -10204,7 +10451,7 @@ static void ImGui::NavEndFrame()
bb_rel.TranslateY(-bb_rel.GetHeight());
clip_dir = ImGuiDir_Up;
}
- NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
+ do_forward = true;
}
if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
{
@@ -10214,28 +10461,34 @@ static void ImGui::NavEndFrame()
bb_rel.TranslateY(+bb_rel.GetHeight());
clip_dir = ImGuiDir_Down;
}
- NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
+ do_forward = true;
}
+ const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
{
bb_rel.Min.y = bb_rel.Max.y =
- ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y;
+ ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y + decoration_up_height;
if (move_flags & ImGuiNavMoveFlags_WrapY)
{
bb_rel.TranslateX(-bb_rel.GetWidth());
clip_dir = ImGuiDir_Left;
}
- NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
+ do_forward = true;
}
if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
{
- bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
+ bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y + decoration_up_height;
if (move_flags & ImGuiNavMoveFlags_WrapY)
{
bb_rel.TranslateX(+bb_rel.GetWidth());
clip_dir = ImGuiDir_Right;
}
- NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
+ do_forward = true;
+ }
+ if (do_forward)
+ {
+ window->NavRectRel[g.NavLayer] = bb_rel;
+ NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags, g.NavMoveScrollFlags);
}
}
}
@@ -10245,6 +10498,7 @@ static int ImGui::FindWindowFocusIndex(ImGuiWindow* window)
ImGuiContext& g = *GImGui;
IM_UNUSED(g);
int order = window->FocusOrder;
+ IM_ASSERT(window->RootWindow == window); // No child window (not testing _ChildWindow because of docking)
IM_ASSERT(g.WindowsFocusOrder[order] == window);
return order;
}
@@ -10299,8 +10553,9 @@ static void ImGui::NavUpdateWindowing()
}
// Start CTRL-TAB or Square+L/R window selection
- bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
- bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && io.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
+ const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
+ const bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
+ const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && nav_keyboard_active && io.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab);
if (start_windowing_with_gamepad || start_windowing_with_keyboard)
if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
{
@@ -10351,7 +10606,7 @@ static void ImGui::NavUpdateWindowing()
// Keyboard: Press and Release ALT to toggle menu layer
// - Testing that only Alt is tested prevents Alt+Shift or AltGR from toggling menu layer.
// - AltGR is normally Alt+Ctrl but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl). But even on keyboards without AltGR we don't want Alt+Ctrl to open menu anyway.
- if (io.KeyMods == ImGuiKeyModFlags_Alt && (io.KeyModsPrev & ImGuiKeyModFlags_Alt) == 0)
+ if (nav_keyboard_active && io.KeyMods == ImGuiKeyModFlags_Alt && (io.KeyModsPrev & ImGuiKeyModFlags_Alt) == 0)
{
g.NavWindowingToggleLayer = true;
g.NavInputSource = ImGuiInputSource_Keyboard;
@@ -10441,15 +10696,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);
+ }
}
}
@@ -10544,14 +10801,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:
@@ -10562,10 +10821,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.
@@ -10731,7 +10986,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;
@@ -11464,6 +11719,10 @@ void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* view
static void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport)
{
+ // Abandon viewport
+ if (window->ViewportOwned && window->Viewport->Window == window)
+ window->Viewport->Size = ImVec2(0.0f, 0.0f);
+
window->Viewport = viewport;
window->ViewportId = viewport->ID;
window->ViewportOwned = (viewport->Window == window);
@@ -11496,6 +11755,7 @@ static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImG
if (GetWindowAlwaysWantOwnViewport(window))
return false;
+ // FIXME: Can't use g.WindowsFocusOrder[] for root windows only as we care about Z order. If we maintained a DisplayOrder along with FocusOrder we could..
for (int n = 0; n < g.Windows.Size; n++)
{
ImGuiWindow* window_behind = g.Windows[n];
@@ -11629,22 +11889,7 @@ static void ImGui::UpdateViewportsNewFrame()
// Erase unused viewports
if (n > 0 && viewport->LastFrameActive < g.FrameCount - 2)
{
- // Clear references to this viewport in windows (window->ViewportId becomes the master data)
- for (int window_n = 0; window_n < g.Windows.Size; window_n++)
- if (g.Windows[window_n]->Viewport == viewport)
- {
- g.Windows[window_n]->Viewport = NULL;
- g.Windows[window_n]->ViewportOwned = false;
- }
- if (viewport == g.MouseLastHoveredViewport)
- g.MouseLastHoveredViewport = NULL;
- g.Viewports.erase(g.Viewports.Data + n);
-
- // Destroy
- IMGUI_DEBUG_LOG_VIEWPORT("Delete Viewport %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a");
- DestroyPlatformWindow(viewport); // In most circumstances the platform window will already be destroyed here.
- IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false);
- IM_DELETE(viewport);
+ DestroyViewport(viewport);
n--;
continue;
}
@@ -11676,7 +11921,7 @@ static void ImGui::UpdateViewportsNewFrame()
// Reset alpha every frame. Users of transparency (docking) needs to request a lower alpha back.
viewport->Alpha = 1.0f;
- // Translate imgui windows when a Host Viewport has been moved
+ // Translate Dear ImGui windows when a Host Viewport has been moved
// (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!)
const ImVec2 viewport_delta_pos = viewport->Pos - viewport->LastPos;
if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (viewport_delta_pos.x != 0.0f || viewport_delta_pos.y != 0.0f))
@@ -11751,7 +11996,8 @@ static void ImGui::UpdateViewportsNewFrame()
// Update mouse reference viewport
// (when moving a window we aim at its viewport, but this will be overwritten below if we go in drag and drop mode)
- if (g.MovingWindow)
+ // (MovingViewport->Viewport will be NULL in the rare situation where the window disappared while moving, set UpdateMouseMovingWindowNewFrame() for details)
+ if (g.MovingWindow && g.MovingWindow->Viewport)
g.MouseViewport = g.MovingWindow->Viewport;
else
g.MouseViewport = g.MouseLastHoveredViewport;
@@ -11855,6 +12101,30 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const
return viewport;
}
+static void ImGui::DestroyViewport(ImGuiViewportP* viewport)
+{
+ // Clear references to this viewport in windows (window->ViewportId becomes the master data)
+ ImGuiContext& g = *GImGui;
+ for (int window_n = 0; window_n < g.Windows.Size; window_n++)
+ {
+ ImGuiWindow* window = g.Windows[window_n];
+ if (window->Viewport != viewport)
+ continue;
+ window->Viewport = NULL;
+ window->ViewportOwned = false;
+ }
+ if (viewport == g.MouseLastHoveredViewport)
+ g.MouseLastHoveredViewport = NULL;
+
+ // Destroy
+ IMGUI_DEBUG_LOG_VIEWPORT("Delete Viewport %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a");
+ DestroyPlatformWindow(viewport); // In most circumstances the platform window will already be destroyed here.
+ IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false);
+ IM_ASSERT(g.Viewports[viewport->Idx] == viewport);
+ g.Viewports.erase(g.Viewports.Data + viewport->Idx);
+ IM_DELETE(viewport);
+}
+
// FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten.
static void ImGui::WindowSelectViewport(ImGuiWindow* window)
{
@@ -11904,8 +12174,15 @@ static void ImGui::WindowSelectViewport(ImGuiWindow* window)
else if ((flags & ImGuiWindowFlags_ChildWindow) || (flags & ImGuiWindowFlags_ChildMenu))
{
// Always inherit viewport from parent window
+ if (window->DockNode && window->DockNode->HostWindow)
+ IM_ASSERT(window->DockNode->HostWindow->Viewport == window->ParentWindow->Viewport);
window->Viewport = window->ParentWindow->Viewport;
}
+ else if (window->DockNode && window->DockNode->HostWindow)
+ {
+ // This covers the "always inherit viewport from parent window" case for when a window reattach to a node that was just created mid-frame
+ window->Viewport = window->DockNode->HostWindow->Viewport;
+ }
else if (flags & ImGuiWindowFlags_Tooltip)
{
window->Viewport = g.MouseViewport;
@@ -11948,7 +12225,7 @@ static void ImGui::WindowSelectViewport(ImGuiWindow* window)
else
window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor;
}
- else if (window->Viewport && window != window->Viewport->Window && window->Viewport->Window && !(flags & ImGuiWindowFlags_ChildWindow))
+ else if (window->Viewport && window != window->Viewport->Window && window->Viewport->Window && !(flags & ImGuiWindowFlags_ChildWindow) && window->DockNode == NULL)
{
// When called from Begin() we don't have access to a proper version of the Hidden flag yet, so we replicate this code.
const bool will_be_visible = (window->DockIsActive && !window->DockTabIsVisible) ? false : true;
@@ -12357,7 +12634,7 @@ void ImGui::DestroyPlatformWindows()
// | - DockContextProcessDock() - process one docking request
// | - DockNodeUpdate()
// | - DockNodeUpdateForRootNode()
-// | - DockNodeUpdateVisibleFlagAndInactiveChilds()
+// | - DockNodeUpdateFlagsAndCollapse()
// | - DockNodeFindInfo()
// | - destroy unused node or tab bar
// | - create dock node host window
@@ -12490,7 +12767,8 @@ namespace ImGui
static void DockNodeHideHostWindow(ImGuiDockNode* node);
static void DockNodeUpdate(ImGuiDockNode* node);
static void DockNodeUpdateForRootNode(ImGuiDockNode* node);
- static void DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node);
+ static void DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node);
+ static void DockNodeUpdateHasCentralNodeChild(ImGuiDockNode* node);
static void DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window);
static void DockNodeAddTabBar(ImGuiDockNode* node);
static void DockNodeRemoveTabBar(ImGuiDockNode* node);
@@ -13111,7 +13389,13 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node)
DockNodeMoveWindows(new_node, node);
DockSettingsRenameNodeReferences(node->ID, new_node->ID);
for (int n = 0; n < new_node->Windows.Size; n++)
- UpdateWindowParentAndRootLinks(new_node->Windows[n], new_node->Windows[n]->Flags, NULL);
+ {
+ ImGuiWindow* window = new_node->Windows[n];
+ window->Flags &= ~ImGuiWindowFlags_ChildWindow;
+ if (window->ParentWindow)
+ window->ParentWindow->DC.ChildWindows.find_erase(window);
+ UpdateWindowParentAndRootLinks(window, window->Flags, NULL);
+ }
node = new_node;
}
else
@@ -13158,7 +13442,8 @@ bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode*
// - ImGuiDockNodeFindInfoResults
// - DockNodeFindInfo()
// - DockNodeFindWindowByID()
-// - DockNodeUpdateVisibleFlagAndInactiveChilds()
+// - DockNodeUpdateFlagsAndCollapse()
+// - DockNodeUpdateHasCentralNodeFlag()
// - DockNodeUpdateVisibleFlag()
// - DockNodeStartMouseMovingWindow()
// - DockNodeUpdate()
@@ -13188,6 +13473,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id)
State = ImGuiDockNodeState_Unknown;
HostWindow = VisibleWindow = NULL;
CentralNode = OnlyNodeWithWindows = NULL;
+ CountNodeWithWindows = 0;
LastFrameAlive = LastFrameActive = LastFrameFocused = -1;
LastFocusedNodeId = 0;
SelectedTabId = 0;
@@ -13195,7 +13481,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id)
AuthorityForPos = AuthorityForSize = ImGuiDataAuthority_DockNode;
AuthorityForViewport = ImGuiDataAuthority_Auto;
IsVisible = true;
- IsFocused = HasCloseButton = HasWindowMenuButton = false;
+ IsFocused = HasCloseButton = HasWindowMenuButton = HasCentralNodeChild = false;
WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false;
MarkedForPosSizeWrite = false;
}
@@ -13293,7 +13579,10 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window
window->DockNode = NULL;
window->DockIsActive = window->DockTabWantClose = false;
window->DockId = save_dock_id;
- UpdateWindowParentAndRootLinks(window, window->Flags & ~ImGuiWindowFlags_ChildWindow, NULL); // Update immediately
+ window->Flags &= ~ImGuiWindowFlags_ChildWindow;
+ if (window->ParentWindow)
+ window->ParentWindow->DC.ChildWindows.find_erase(window);
+ UpdateWindowParentAndRootLinks(window, window->Flags, NULL); // Update immediately
// Remove window
bool erased = false;
@@ -13304,7 +13593,8 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window
erased = true;
break;
}
- IM_ASSERT(erased);
+ if (!erased)
+ IM_ASSERT(erased);
if (node->VisibleWindow == window)
node->VisibleWindow = NULL;
@@ -13331,6 +13621,7 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window
if (node->HostWindow->ViewportOwned && node->IsRootNode())
{
// Transfer viewport back to the remaining loose window
+ IMGUI_DEBUG_LOG_VIEWPORT("Node %08X transfer Viewport %08X=>%08X for Window '%s'\n", node->ID, node->HostWindow->Viewport->ID, remaining_window->ID, remaining_window->Name);
IM_ASSERT(node->HostWindow->Viewport->Window == node->HostWindow);
node->HostWindow->Viewport->Window = remaining_window;
node->HostWindow->Viewport->ID = remaining_window->ID;
@@ -13421,36 +13712,36 @@ static void ImGui::DockNodeHideHostWindow(ImGuiDockNode* node)
}
// Search function called once by root node in DockNodeUpdate()
-struct ImGuiDockNodeFindInfoResults
+struct ImGuiDockNodeTreeInfo
{
ImGuiDockNode* CentralNode;
ImGuiDockNode* FirstNodeWithWindows;
int CountNodesWithWindows;
//ImGuiWindowClass WindowClassForMerges;
- ImGuiDockNodeFindInfoResults() { CentralNode = FirstNodeWithWindows = NULL; CountNodesWithWindows = 0; }
+ ImGuiDockNodeTreeInfo() { memset(this, 0, sizeof(*this)); }
};
-static void DockNodeFindInfo(ImGuiDockNode* node, ImGuiDockNodeFindInfoResults* results)
+static void DockNodeFindInfo(ImGuiDockNode* node, ImGuiDockNodeTreeInfo* info)
{
if (node->Windows.Size > 0)
{
- if (results->FirstNodeWithWindows == NULL)
- results->FirstNodeWithWindows = node;
- results->CountNodesWithWindows++;
+ if (info->FirstNodeWithWindows == NULL)
+ info->FirstNodeWithWindows = node;
+ info->CountNodesWithWindows++;
}
if (node->IsCentralNode())
{
- IM_ASSERT(results->CentralNode == NULL); // Should be only one
+ IM_ASSERT(info->CentralNode == NULL); // Should be only one
IM_ASSERT(node->IsLeafNode() && "If you get this assert: please submit .ini file + repro of actions leading to this.");
- results->CentralNode = node;
+ info->CentralNode = node;
}
- if (results->CountNodesWithWindows > 1 && results->CentralNode != NULL)
+ if (info->CountNodesWithWindows > 1 && info->CentralNode != NULL)
return;
if (node->ChildNodes[0])
- DockNodeFindInfo(node->ChildNodes[0], results);
+ DockNodeFindInfo(node->ChildNodes[0], info);
if (node->ChildNodes[1])
- DockNodeFindInfo(node->ChildNodes[1], results);
+ DockNodeFindInfo(node->ChildNodes[1], info);
}
static ImGuiWindow* ImGui::DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id)
@@ -13464,7 +13755,7 @@ static ImGuiWindow* ImGui::DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID i
// - Remove inactive windows/nodes.
// - Update visibility flag.
-static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node)
+static void ImGui::DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(node->ParentNode == NULL || node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node);
@@ -13477,12 +13768,13 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod
// There is the possibility that one of our child becoming empty will delete itself and moving its sibling contents into 'node'.
// If 'node->ChildNode[0]' delete itself, then 'node->ChildNode[1]->Windows' will be moved into 'node'
// If 'node->ChildNode[1]' delete itself, then 'node->ChildNode[0]->Windows' will be moved into 'node' and the "remove inactive windows" loop will have run twice on those windows (harmless)
+ node->HasCentralNodeChild = false;
if (node->ChildNodes[0])
- DockNodeUpdateVisibleFlagAndInactiveChilds(node->ChildNodes[0]);
+ DockNodeUpdateFlagsAndCollapse(node->ChildNodes[0]);
if (node->ChildNodes[1])
- DockNodeUpdateVisibleFlagAndInactiveChilds(node->ChildNodes[1]);
+ DockNodeUpdateFlagsAndCollapse(node->ChildNodes[1]);
- // Remove inactive windows
+ // Remove inactive windows, collapse nodes
// Merge node flags overrides stored in windows
node->LocalFlagsInWindows = ImGuiDockNodeFlags_None;
for (int window_n = 0; window_n < node->Windows.Size; window_n++)
@@ -13507,13 +13799,12 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod
}
DockNodeRemoveWindow(node, window, node->ID);
window_n--;
+ continue;
}
- else
- {
- // FIXME-DOCKING: Missing policies for conflict resolution, hence the "Experimental" tag on this.
- //node->LocalFlagsInWindow &= ~window->WindowClass.DockNodeFlagsOverrideClear;
- node->LocalFlagsInWindows |= window->WindowClass.DockNodeFlagsOverrideSet;
- }
+
+ // FIXME-DOCKING: Missing policies for conflict resolution, hence the "Experimental" tag on this.
+ //node->LocalFlagsInWindow &= ~window->WindowClass.DockNodeFlagsOverrideClear;
+ node->LocalFlagsInWindows |= window->WindowClass.DockNodeFlagsOverrideSet;
}
node->UpdateMergedFlags();
@@ -13537,6 +13828,25 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod
DockNodeUpdateVisibleFlag(node);
}
+// This is rarely called as DockNodeUpdateForRootNode() generally does it most frames.
+static void ImGui::DockNodeUpdateHasCentralNodeChild(ImGuiDockNode* node)
+{
+ node->HasCentralNodeChild = false;
+ if (node->ChildNodes[0])
+ DockNodeUpdateHasCentralNodeChild(node->ChildNodes[0]);
+ if (node->ChildNodes[1])
+ DockNodeUpdateHasCentralNodeChild(node->ChildNodes[1]);
+ if (node->IsRootNode())
+ {
+ ImGuiDockNode* mark_node = node->CentralNode;
+ while (mark_node)
+ {
+ mark_node->HasCentralNodeChild = true;
+ mark_node = mark_node->ParentNode;
+ }
+ }
+}
+
static void ImGui::DockNodeUpdateVisibleFlag(ImGuiDockNode* node)
{
// Update visibility flag
@@ -13560,22 +13870,23 @@ static void ImGui::DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWind
// Update CentralNode, OnlyNodeWithWindows, LastFocusedNodeID. Copy window class.
static void ImGui::DockNodeUpdateForRootNode(ImGuiDockNode* node)
{
- DockNodeUpdateVisibleFlagAndInactiveChilds(node);
+ DockNodeUpdateFlagsAndCollapse(node);
- // FIXME-DOCK: Merge this scan into the one above.
// - Setup central node pointers
// - Find if there's only a single visible window in the hierarchy (in which case we need to display a regular title bar -> FIXME-DOCK: that last part is not done yet!)
- ImGuiDockNodeFindInfoResults results;
- DockNodeFindInfo(node, &results);
- node->CentralNode = results.CentralNode;
- node->OnlyNodeWithWindows = (results.CountNodesWithWindows == 1) ? results.FirstNodeWithWindows : NULL;
- if (node->LastFocusedNodeId == 0 && results.FirstNodeWithWindows != NULL)
- node->LastFocusedNodeId = results.FirstNodeWithWindows->ID;
+ // Cannot merge this with DockNodeUpdateFlagsAndCollapse() because FirstNodeWithWindows is found after window removal and child collapsing
+ ImGuiDockNodeTreeInfo info;
+ DockNodeFindInfo(node, &info);
+ node->CentralNode = info.CentralNode;
+ node->OnlyNodeWithWindows = (info.CountNodesWithWindows == 1) ? info.FirstNodeWithWindows : NULL;
+ node->CountNodeWithWindows = info.CountNodesWithWindows;
+ if (node->LastFocusedNodeId == 0 && info.FirstNodeWithWindows != NULL)
+ node->LastFocusedNodeId = info.FirstNodeWithWindows->ID;
// Copy the window class from of our first window so it can be used for proper dock filtering.
// When node has mixed windows, prioritize the class with the most constraint (DockingAllowUnclassed = false) as the reference to copy.
// FIXME-DOCK: We don't recurse properly, this code could be reworked to work from DockNodeUpdateScanRec.
- if (ImGuiDockNode* first_node_with_windows = results.FirstNodeWithWindows)
+ if (ImGuiDockNode* first_node_with_windows = info.FirstNodeWithWindows)
{
node->WindowClass = first_node_with_windows->Windows[0]->WindowClass;
for (int n = 1; n < first_node_with_windows->Windows.Size; n++)
@@ -13585,6 +13896,13 @@ static void ImGui::DockNodeUpdateForRootNode(ImGuiDockNode* node)
break;
}
}
+
+ ImGuiDockNode* mark_node = node->CentralNode;
+ while (mark_node)
+ {
+ mark_node->HasCentralNodeChild = true;
+ mark_node = mark_node->ParentNode;
+ }
}
static void DockNodeSetupHostWindow(ImGuiDockNode* node, ImGuiWindow* host_window)
@@ -13618,9 +13936,14 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node)
// Early out for hidden root dock nodes (when all DockId references are in inactive windows, or there is only 1 floating window holding on the DockId)
bool want_to_hide_host_window = false;
- if (node->Windows.Size <= 1 && node->IsFloatingNode() && node->IsLeafNode())
- if (!g.IO.ConfigDockingAlwaysTabBar && (node->Windows.Size == 0 || !node->Windows[0]->WindowClass.DockingAlwaysTabBar))
+ if (node->IsFloatingNode())
+ {
+ if (node->Windows.Size <= 1 && node->IsLeafNode())
+ if (!g.IO.ConfigDockingAlwaysTabBar && (node->Windows.Size == 0 || !node->Windows[0]->WindowClass.DockingAlwaysTabBar))
+ want_to_hide_host_window = true;
+ if (node->CountNodeWithWindows == 0)
want_to_hide_host_window = true;
+ }
if (want_to_hide_host_window)
{
if (node->Windows.Size == 1)
@@ -14638,6 +14961,7 @@ void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG
DockNodeMoveWindows(parent_node->ChildNodes[split_inheritor_child_idx], parent_node);
DockSettingsRenameNodeReferences(parent_node->ID, parent_node->ChildNodes[split_inheritor_child_idx]->ID);
+ DockNodeUpdateHasCentralNodeChild(DockNodeGetRootNode(parent_node));
DockNodeTreeUpdatePosSize(parent_node, parent_node->Pos, parent_node->Size);
// Flags transfer (e.g. this is where we transfer the ImGuiDockNodeFlags_CentralNode property)
@@ -14703,6 +15027,7 @@ void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG
}
// Update Pos/Size for a node hierarchy (don't affect child Windows yet)
+// (Depth-first, Pre-Order)
void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, bool only_write_to_marked_nodes)
{
// During the regular dock node update we write to all nodes.
@@ -14732,6 +15057,10 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si
ImGuiContext& g = *GImGui;
const float size_min_each = ImFloor(ImMin(size_avail, g.Style.WindowMinSize[axis] * 2.0f) * 0.5f);
+ // FIXME: Blocks 2) and 3) are essentially doing nearly the same thing.
+ // Difference are: write-back to SizeRef; application of a minimum size; rounding before ImFloor()
+ // Clarify and rework differences between Size & SizeRef and purpose of WantLockSizeOnce
+
// 2) Process locked absolute size (during a splitter resize we preserve the child of nodes not touching the splitter edge)
if (child_0->WantLockSizeOnce && !child_1->WantLockSizeOnce)
{
@@ -14749,19 +15078,19 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si
{
// FIXME-DOCK: We cannot honor the requested size, so apply ratio.
// Currently this path will only be taken if code programmatically sets WantLockSizeOnce
- float ratio_0 = child_0_size[axis] / (child_0_size[axis] + child_1_size[axis]);
- child_0_size[axis] = child_0->SizeRef[axis] = ImFloor(size_avail * ratio_0);
+ float split_ratio = child_0_size[axis] / (child_0_size[axis] + child_1_size[axis]);
+ child_0_size[axis] = child_0->SizeRef[axis] = ImFloor(size_avail * split_ratio);
child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]);
IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f);
}
// 3) If one window is the central node (~ use remaining space, should be made explicit!), use explicit size from the other, and remainder for the central node
- else if (child_1->IsCentralNode() && child_0->SizeRef[axis] != 0.0f)
+ else if (child_0->SizeRef[axis] != 0.0f && child_1->HasCentralNodeChild)
{
child_0_size[axis] = ImMin(size_avail - size_min_each, child_0->SizeRef[axis]);
child_1_size[axis] = (size_avail - child_0_size[axis]);
}
- else if (child_0->IsCentralNode() && child_1->SizeRef[axis] != 0.0f)
+ else if (child_1->SizeRef[axis] != 0.0f && child_0->HasCentralNodeChild)
{
child_1_size[axis] = ImMin(size_avail - size_min_each, child_1->SizeRef[axis]);
child_0_size[axis] = (size_avail - child_1_size[axis]);
@@ -14770,7 +15099,7 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si
{
// 4) Otherwise distribute according to the relative ratio of each SizeRef value
float split_ratio = child_0->SizeRef[axis] / (child_0->SizeRef[axis] + child_1->SizeRef[axis]);
- child_0_size[axis] = ImMax(size_min_each, ImFloor(size_avail * split_ratio + 0.5F));
+ child_0_size[axis] = ImMax(size_min_each, ImFloor(size_avail * split_ratio + 0.5f));
child_1_size[axis] = (size_avail - child_0_size[axis]);
}
@@ -14799,6 +15128,7 @@ static void DockNodeTreeUpdateSplitterFindTouchingNode(ImGuiDockNode* node, ImGu
DockNodeTreeUpdateSplitterFindTouchingNode(node->ChildNodes[1], axis, side, touching_nodes);
}
+// (Depth-First, Pre-Order)
void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node)
{
if (node->IsLeafNode())
@@ -14833,7 +15163,7 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node)
//bb.Max[axis] -= 1;
PushID(node->ID);
- // Gather list of nodes that are touching the splitter line. Find resizing limits based on those nodes.
+ // Find resizing limits by gathering list of nodes that are touching the splitter line.
ImVector<ImGuiDockNode*> touching_nodes[2];
float min_size = g.Style.WindowMinSize[axis];
float resize_limits[2];
@@ -14841,9 +15171,8 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node)
resize_limits[1] = node->ChildNodes[1]->Pos[axis] + node->ChildNodes[1]->Size[axis] - min_size;
ImGuiID splitter_id = GetID("##Splitter");
- if (g.ActiveId == splitter_id)
+ if (g.ActiveId == splitter_id) // Only process when splitter is active
{
- // Only process when splitter is active
DockNodeTreeUpdateSplitterFindTouchingNode(child_0, axis, 1, &touching_nodes[0]);
DockNodeTreeUpdateSplitterFindTouchingNode(child_1, axis, 0, &touching_nodes[1]);
for (int touching_node_n = 0; touching_node_n < touching_nodes[0].Size; touching_node_n++)
@@ -14851,14 +15180,18 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node)
for (int touching_node_n = 0; touching_node_n < touching_nodes[1].Size; touching_node_n++)
resize_limits[1] = ImMin(resize_limits[1], touching_nodes[1][touching_node_n]->Rect().Max[axis] - min_size);
+ // [DEBUG] Render touching nodes & limits
/*
- // [DEBUG] Render limits
ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport());
for (int n = 0; n < 2; n++)
+ {
+ for (int touching_node_n = 0; touching_node_n < touching_nodes[n].Size; touching_node_n++)
+ draw_list->AddRect(touching_nodes[n][touching_node_n]->Pos, touching_nodes[n][touching_node_n]->Pos + touching_nodes[n][touching_node_n]->Size, IM_COL32(0, 255, 0, 255));
if (axis == ImGuiAxis_X)
draw_list->AddLine(ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y), ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y + node->ChildNodes[n]->Size.y), IM_COL32(255, 0, 255, 255), 3.0f);
else
draw_list->AddLine(ImVec2(node->ChildNodes[n]->Pos.x, resize_limits[n]), ImVec2(node->ChildNodes[n]->Pos.x + node->ChildNodes[n]->Size.x, resize_limits[n]), IM_COL32(255, 0, 255, 255), 3.0f);
+ }
*/
}
@@ -15534,6 +15867,7 @@ void ImGui::DockBuilderCopyDockSpace(ImGuiID src_dockspace_id, ImGuiID dst_docks
}
}
+// FIXME-DOCK: This is awkward because in series of split user is likely to loose access to its root node.
void ImGui::DockBuilderFinish(ImGuiID root_id)
{
ImGuiContext* ctx = GImGui;
@@ -15597,6 +15931,7 @@ static ImGuiDockNode* ImGui::DockContextBindNodeToWindow(ImGuiContext* ctx, ImGu
ancestor_node = ancestor_node->ParentNode;
}
IM_ASSERT(ancestor_node->Size.x > 0.0f && ancestor_node->Size.y > 0.0f);
+ DockNodeUpdateHasCentralNodeChild(DockNodeGetRootNode(ancestor_node));
DockNodeTreeUpdatePosSize(ancestor_node, ancestor_node->Pos, ancestor_node->Size, true);
}
@@ -16292,22 +16627,23 @@ namespace ImGui { void ShowFontAtlas(ImFontAtlas* atlas); }
void ImGui::ShowMetricsWindow(bool* p_open)
{
- if (!Begin("Dear ImGui Metrics/Debugger", p_open))
+ ImGuiContext& g = *GImGui;
+ ImGuiIO& io = g.IO;
+ ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
+ if (cfg->ShowStackTool)
+ ShowStackToolWindow(&cfg->ShowStackTool);
+
+ if (!Begin("Dear ImGui Metrics/Debugger", p_open) || GetCurrentWindow()->BeginCount > 1)
{
End();
return;
}
- ImGuiContext& g = *GImGui;
- ImGuiIO& io = g.IO;
- ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
-
// Basic info
Text("Dear ImGui %s", GetVersion());
Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
- Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
- Text("%d active allocations", io.MetricsActiveAllocations);
+ Text("%d visible windows, %d active allocations", io.MetricsRenderWindows, io.MetricsActiveAllocations);
//SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; }
Separator();
@@ -16361,11 +16697,10 @@ void ImGui::ShowMetricsWindow(bool* p_open)
// Tools
if (TreeNode("Tools"))
{
- // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
- if (Button("Item Picker.."))
- DebugStartItemPicker();
+ // Stack Tool is your best friend!
+ Checkbox("Show stack tool", &cfg->ShowStackTool);
SameLine();
- MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash.");
+ MetricsHelpMarker("You can also call ImGui::ShowStackToolWindow() from your code.");
Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder);
Checkbox("Show windows rectangles", &cfg->ShowWindowsRects);
@@ -16383,8 +16718,6 @@ void ImGui::ShowMetricsWindow(bool* p_open)
}
Unindent();
}
- Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh);
- Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes);
Checkbox("Show tables rectangles", &cfg->ShowTablesRects);
SameLine();
@@ -16431,6 +16764,12 @@ void ImGui::ShowMetricsWindow(bool* p_open)
}
}
+ // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
+ if (Button("Item Picker.."))
+ DebugStartItemPicker();
+ SameLine();
+ MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash.");
+
TreePop();
}
@@ -16444,6 +16783,8 @@ void ImGui::ShowMetricsWindow(bool* p_open)
drawlist_count += g.Viewports[viewport_i]->DrawDataBuilder.GetDrawListCount();
if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count))
{
+ Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh);
+ Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes);
for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++)
{
ImGuiViewportP* viewport = g.Viewports[viewport_i];
@@ -16663,7 +17004,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");
@@ -16830,11 +17172,12 @@ void ImGui::DebugNodeDockNode(ImGuiDockNode* node, const char* label)
DebugNodeWindow(node->HostWindow, "HostWindow");
DebugNodeWindow(node->VisibleWindow, "VisibleWindow");
BulletText("SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X", node->SelectedTabId, node->LastFocusedNodeId);
- BulletText("Misc:%s%s%s%s%s",
+ BulletText("Misc:%s%s%s%s%s%s",
node->IsDockSpace() ? " IsDockSpace" : "",
node->IsCentralNode() ? " IsCentralNode" : "",
is_alive ? " IsAlive" : "", is_active ? " IsActive" : "",
- node->WantLockSizeOnce ? " WantLockSizeOnce" : "");
+ node->WantLockSizeOnce ? " WantLockSizeOnce" : "",
+ node->HasCentralNodeChild ? " HasCentralNodeChild" : "");
if (TreeNode("flags", "Flags Merged: 0x%04X, Local: 0x%04X, InWindows: 0x%04X, Shared: 0x%04X", node->MergedFlags, node->LocalFlags, node->LocalFlagsInWindows, node->SharedFlags))
{
if (BeginTable("flags", 4))
@@ -17255,6 +17598,186 @@ void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* la
TreePop();
}
+//-----------------------------------------------------------------------------
+// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, STACK TOOL)
+//-----------------------------------------------------------------------------
+
+// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
+void ImGui::UpdateDebugToolItemPicker()
+{
+ ImGuiContext& g = *GImGui;
+ g.DebugItemPickerBreakId = 0;
+ if (!g.DebugItemPickerActive)
+ return;
+
+ const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
+ SetMouseCursor(ImGuiMouseCursor_Hand);
+ if (IsKeyPressedMap(ImGuiKey_Escape))
+ g.DebugItemPickerActive = false;
+ if (IsMouseClicked(0) && hovered_id)
+ {
+ g.DebugItemPickerBreakId = hovered_id;
+ g.DebugItemPickerActive = false;
+ }
+ SetNextWindowBgAlpha(0.60f);
+ BeginTooltip();
+ Text("HoveredId: 0x%08X", hovered_id);
+ Text("Press ESC to abort picking.");
+ TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!");
+ EndTooltip();
+}
+
+// [DEBUG] Stack Tool: update queries. Called by NewFrame()
+void ImGui::UpdateDebugToolStackQueries()
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiStackTool* tool = &g.DebugStackTool;
+
+ // Clear hook when stack tool is not visible
+ g.DebugHookIdInfo = 0;
+ if (g.FrameCount != tool->LastActiveFrame + 1)
+ return;
+
+ // Update queries. The steps are: -1: query Stack, >= 0: query each stack item
+ // We can only perform 1 ID Info query every frame. This is designed so the GetID() tests are cheap and constant-time
+ const ImGuiID query_id = g.ActiveId ? g.ActiveId : g.HoveredIdPreviousFrame;
+ if (tool->QueryId != query_id)
+ {
+ tool->QueryId = query_id;
+ tool->StackLevel = -1;
+ tool->Results.resize(0);
+ }
+ if (query_id == 0)
+ return;
+
+ // Advance to next stack level when we got our result, or after 2 frames (in case we never get a result)
+ int stack_level = tool->StackLevel;
+ if (stack_level >= 0 && stack_level < tool->Results.Size)
+ if (tool->Results[stack_level].QuerySuccess || tool->Results[stack_level].QueryFrameCount > 2)
+ tool->StackLevel++;
+
+ // Update hook
+ stack_level = tool->StackLevel;
+ if (stack_level == -1)
+ g.DebugHookIdInfo = query_id;
+ if (stack_level >= 0 && stack_level < tool->Results.Size)
+ {
+ g.DebugHookIdInfo = tool->Results[stack_level].ID;
+ tool->Results[stack_level].QueryFrameCount++;
+ }
+}
+
+// [DEBUG] Stack tool: hooks called by GetID() family functions
+void ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end)
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+ ImGuiStackTool* tool = &g.DebugStackTool;
+
+ // Step 0: stack query
+ // This assume that the ID was computed with the current ID stack, which tends to be the case for our widget.
+ if (tool->StackLevel == -1)
+ {
+ tool->StackLevel++;
+ tool->Results.resize(window->IDStack.Size + 1, ImGuiStackLevelInfo());
+ for (int n = 0; n < window->IDStack.Size + 1; n++)
+ tool->Results[n].ID = (n < window->IDStack.Size) ? window->IDStack[n] : id;
+ return;
+ }
+
+ // Step 1+: query for individual level
+ IM_ASSERT(tool->StackLevel >= 0);
+ if (tool->StackLevel != window->IDStack.Size)
+ return;
+ ImGuiStackLevelInfo* info = &tool->Results[tool->StackLevel];
+ IM_ASSERT(info->ID == id && info->QueryFrameCount > 0);
+
+ int data_len;
+ switch (data_type)
+ {
+ case ImGuiDataType_S32:
+ ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "%d", (int)(intptr_t)data_id);
+ break;
+ case ImGuiDataType_String:
+ data_len = data_id_end ? (int)((const char*)data_id_end - (const char*)data_id) : (int)strlen((const char*)data_id);
+ ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "\"%.*s\"", data_len, (const char*)data_id);
+ break;
+ case ImGuiDataType_Pointer:
+ ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "(void*)0x%p", data_id);
+ break;
+ case ImGuiDataType_ID:
+ if (info->Desc[0] == 0) // PushOverrideID() is often used to avoid hashing twice, which would lead to 2 calls to DebugHookIdInfo(). We prioritize the first one.
+ ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "0x%08X [override]", id);
+ break;
+ default:
+ IM_ASSERT(0);
+ }
+ info->QuerySuccess = true;
+}
+
+// Stack Tool: Display UI
+void ImGui::ShowStackToolWindow(bool* p_open)
+{
+ if (!Begin("Dear ImGui Stack Tool", p_open) || GetCurrentWindow()->BeginCount > 1)
+ {
+ End();
+ return;
+ }
+
+ // Display hovered/active status
+ ImGuiContext& g = *GImGui;
+ const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
+ const ImGuiID active_id = g.ActiveId;
+#ifdef IMGUI_ENABLE_TEST_ENGINE
+ Text("HoveredId: 0x%08X (\"%s\"), ActiveId: 0x%08X (\"%s\")", hovered_id, hovered_id ? ImGuiTestEngine_FindItemDebugLabel(&g, hovered_id) : "", active_id, active_id ? ImGuiTestEngine_FindItemDebugLabel(&g, active_id) : "");
+#else
+ Text("HoveredId: 0x%08X, ActiveId: 0x%08X", hovered_id, active_id);
+#endif
+ SameLine();
+ MetricsHelpMarker("Hover an item with the mouse to display elements of the ID Stack leading to the item's final ID.\nEach level of the stack correspond to a PushID() call.\nAll levels of the stack are hashed together to make the final ID of a widget (ID displayed at the bottom level of the stack).\nRead FAQ entry about the ID stack for details.");
+
+ // Display decorated stack
+ ImGuiStackTool* tool = &g.DebugStackTool;
+ tool->LastActiveFrame = g.FrameCount;
+ if (tool->Results.Size > 0 && BeginTable("##table", 3, ImGuiTableFlags_Borders))
+ {
+ const float id_width = CalcTextSize("0xDDDDDDDD").x;
+ TableSetupColumn("Seed", ImGuiTableColumnFlags_WidthFixed, id_width);
+ TableSetupColumn("PushID", ImGuiTableColumnFlags_WidthStretch);
+ TableSetupColumn("Result", ImGuiTableColumnFlags_WidthFixed, id_width);
+ TableHeadersRow();
+ for (int n = 0; n < tool->Results.Size; n++)
+ {
+ ImGuiStackLevelInfo* info = &tool->Results[n];
+ TableNextColumn();
+ Text("0x%08X", (n > 0) ? tool->Results[n - 1].ID : 0);
+
+ TableNextColumn();
+ ImGuiWindow* window = (info->Desc[0] == 0 && n == 0) ? FindWindowByID(info->ID) : NULL;
+ if (window) // Source: window name (because the root ID don't call GetID() and so doesn't get hooked)
+ Text("\"%s\" [window]", window->Name);
+ else if (info->QuerySuccess) // Source: GetID() hooks (prioritize over ItemInfo() because we frequently use patterns like: PushID(str), Button("") where they both have same id)
+ TextUnformatted(info->Desc);
+ else if (tool->StackLevel >= tool->Results.Size) // Only start using fallback below when all queries are done, so during queries we don't flickering ??? markers.
+ {
+#ifdef IMGUI_ENABLE_TEST_ENGINE
+ if (const char* label = ImGuiTestEngine_FindItemDebugLabel(&g, info->ID)) // Source: ImGuiTestEngine's ItemInfo()
+ Text("??? \"%s\"", label);
+ else
+#endif
+ TextUnformatted("???");
+ }
+
+ TableNextColumn();
+ Text("0x%08X", info->ID);
+ if (n == tool->Results.Size - 1)
+ TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_Header));
+ }
+ EndTable();
+ }
+ End();
+}
+
#else
void ImGui::ShowMetricsWindow(bool*) {}
@@ -17270,7 +17793,12 @@ void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {}
void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>*, const char*) {}
void ImGui::DebugNodeViewport(ImGuiViewportP*) {}
-#endif
+void ImGui::ShowStackToolWindow(bool*) {}
+void ImGui::DebugHookIdInfo(ImGuiID, ImGuiDataType, const void*, const void*) {}
+void ImGui::UpdateDebugToolItemPicker() {}
+void ImGui::UpdateDebugToolStackQueries() {}
+
+#endif // #ifndef IMGUI_DISABLE_METRICS_WINDOW
//-----------------------------------------------------------------------------
diff --git a/imgui/imgui.h b/imgui/imgui.h
index 99106b57..d04b5e8e 100644
--- a/imgui/imgui.h
+++ b/imgui/imgui.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.84
+// dear imgui, v1.85
// (headers)
// Help:
@@ -15,7 +15,10 @@
// - Wiki https://github.com/ocornut/imgui/wiki (lots of good stuff there)
// - Glossary https://github.com/ocornut/imgui/wiki/Glossary
// - Issues & support https://github.com/ocornut/imgui/issues
-// - Discussions https://github.com/ocornut/imgui/discussions
+
+// Getting Started?
+// - For first-time users having issues compiling/linking/running or issues loading fonts:
+// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above.
/*
@@ -61,8 +64,8 @@ Index of this file:
// Version
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens)
-#define IMGUI_VERSION "1.84.1"
-#define IMGUI_VERSION_NUM 18403
+#define IMGUI_VERSION "1.85"
+#define IMGUI_VERSION_NUM 18500
#define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx))
#define IMGUI_HAS_TABLE
#define IMGUI_HAS_VIEWPORT // Viewport WIP branch
@@ -92,7 +95,7 @@ Index of this file:
#endif
// Helper Macros - IM_FMTARGS, IM_FMTLIST: Apply printf-style warnings to our formatting functions.
-#if !defined(IMGUI_USE_STB_SPRINTF) && defined(__MINGW32__)
+#if !defined(IMGUI_USE_STB_SPRINTF) && defined(__MINGW32__) && !defined(__clang__)
#define IM_FMTARGS(FMT) __attribute__((format(gnu_printf, FMT, FMT+1)))
#define IM_FMTLIST(FMT) __attribute__((format(gnu_printf, FMT, 0)))
#elif !defined(IMGUI_USE_STB_SPRINTF) && (defined(__clang__) || defined(__GNUC__))
@@ -166,7 +169,7 @@ struct ImGuiTextFilter; // Helper to parse and apply text filters (e
struct ImGuiViewport; // A Platform Window (always 1 unless multi-viewport are enabled. One per platform window to output to). In the future may represent Platform Monitor
struct ImGuiWindowClass; // Window class (rare/advanced uses: provide hints to the platform backend via altered viewport flags and parent/child info)
-// Enums/Flags (declared as int for compatibility with old C++, to allow using as flags and to not pollute the top of this file)
+// Enums/Flags (declared as int for compatibility with old C++, to allow using as flags without overhead, and to not pollute the top of this file)
// - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists!
// In Visual Studio IDE: CTRL+comma ("Edit.NavigateTo") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
// With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments.
@@ -207,27 +210,22 @@ typedef int ImGuiTreeNodeFlags; // -> enum ImGuiTreeNodeFlags_ // Flags: f
typedef int ImGuiViewportFlags; // -> enum ImGuiViewportFlags_ // Flags: for ImGuiViewport
typedef int ImGuiWindowFlags; // -> enum ImGuiWindowFlags_ // Flags: for Begin(), BeginChild()
-// Other types
-#ifndef ImTextureID // ImTextureID [configurable type: override in imconfig.h with '#define ImTextureID xxx']
-typedef void* ImTextureID; // User data for rendering backend to identify a texture. This is whatever to you want it to be! read the FAQ about ImTextureID for details.
+// ImTexture: user data for renderer backend to identify a texture [Compile-time configurable type]
+// - To use something else than an opaque void* pointer: override with e.g. '#define ImTextureID MyTextureType*' in your imconfig.h file.
+// - This can be whatever to you want it to be! read the FAQ about ImTextureID for details.
+#ifndef ImTextureID
+typedef void* ImTextureID; // Default: store a pointer or an integer fitting in a pointer (most renderer backends are ok with that)
#endif
-typedef unsigned int ImGuiID; // A unique ID used by widgets, typically hashed from a stack of string.
-typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData* data); // Callback function for ImGui::InputText()
-typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data); // Callback function for ImGui::SetNextWindowSizeConstraints()
-typedef void* (*ImGuiMemAllocFunc)(size_t sz, void* user_data); // Function signature for ImGui::SetAllocatorFunctions()
-typedef void (*ImGuiMemFreeFunc)(void* ptr, void* user_data); // Function signature for ImGui::SetAllocatorFunctions()
-// Character types
-// (we generally use UTF-8 encoded string in the API. This is storage specifically for a decoded character used for keyboard input and display)
-typedef unsigned short ImWchar16; // A single decoded U16 character/code point. We encode them as multi bytes UTF-8 when used in strings.
-typedef unsigned int ImWchar32; // A single decoded U32 character/code point. We encode them as multi bytes UTF-8 when used in strings.
-#ifdef IMGUI_USE_WCHAR32 // ImWchar [configurable type: override in imconfig.h with '#define IMGUI_USE_WCHAR32' to support Unicode planes 1-16]
-typedef ImWchar32 ImWchar;
-#else
-typedef ImWchar16 ImWchar;
+// ImDrawIdx: vertex index. [Compile-time configurable type]
+// - To use 16-bit indices + allow large meshes: backend need to set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset' and handle ImDrawCmd::VtxOffset (recommended).
+// - To use 32-bit indices: override with '#define ImDrawIdx unsigned int' in your imconfig.h file.
+#ifndef ImDrawIdx
+typedef unsigned short ImDrawIdx; // Default: 16-bit (for maximum compatibility with renderer backends)
#endif
-// Basic scalar data types
+// Scalar data types
+typedef unsigned int ImGuiID;// A unique ID used by widgets (typically the result of hashing a stack of string)
typedef signed char ImS8; // 8-bit signed integer
typedef unsigned char ImU8; // 8-bit unsigned integer
typedef signed short ImS16; // 16-bit signed integer
@@ -246,7 +244,24 @@ typedef signed long long ImS64; // 64-bit signed integer (post C++11)
typedef unsigned long long ImU64; // 64-bit unsigned integer (post C++11)
#endif
-// 2D vector (often used to store positions or sizes)
+// Character types
+// (we generally use UTF-8 encoded string in the API. This is storage specifically for a decoded character used for keyboard input and display)
+typedef unsigned short ImWchar16; // A single decoded U16 character/code point. We encode them as multi bytes UTF-8 when used in strings.
+typedef unsigned int ImWchar32; // A single decoded U32 character/code point. We encode them as multi bytes UTF-8 when used in strings.
+#ifdef IMGUI_USE_WCHAR32 // ImWchar [configurable type: override in imconfig.h with '#define IMGUI_USE_WCHAR32' to support Unicode planes 1-16]
+typedef ImWchar32 ImWchar;
+#else
+typedef ImWchar16 ImWchar;
+#endif
+
+// Callback and functions types
+typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData* data); // Callback function for ImGui::InputText()
+typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data); // Callback function for ImGui::SetNextWindowSizeConstraints()
+typedef void* (*ImGuiMemAllocFunc)(size_t sz, void* user_data); // Function signature for ImGui::SetAllocatorFunctions()
+typedef void (*ImGuiMemFreeFunc)(void* ptr, void* user_data); // Function signature for ImGui::SetAllocatorFunctions()
+
+// ImVec2: 2D vector used to store positions, sizes etc. [Compile-time configurable type]
+// This is a frequently used type in the API. Consider using IM_VEC2_CLASS_EXTRA to create implicit cast from/to our preferred type.
IM_MSVC_RUNTIME_CHECKS_OFF
struct ImVec2
{
@@ -260,7 +275,7 @@ struct ImVec2
#endif
};
-// 4D vector (often used to store floating-point colors)
+// ImVec4: 4D vector used to store clipping rectangles, colors etc. [Compile-time configurable type]
struct ImVec4
{
float x, y, z, w;
@@ -299,6 +314,7 @@ namespace ImGui
// Demo, Debug, Information
IMGUI_API void ShowDemoWindow(bool* p_open = NULL); // create Demo window. demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application!
IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // create Metrics/Debugger window. display Dear ImGui internals: windows, draw commands, various internal state, etc.
+ IMGUI_API void ShowStackToolWindow(bool* p_open = NULL); // create Stack Tool window. hover items with mouse to query information about the source of their unique ID.
IMGUI_API void ShowAboutWindow(bool* p_open = NULL); // create About window. display Dear ImGui version, credits and build/system information.
IMGUI_API void ShowStyleEditor(ImGuiStyle* ref = NULL); // add style editor block (not a window). you can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it uses the default style)
IMGUI_API bool ShowStyleSelector(const char* label); // add style selector block (not a window), essentially a combo listing the default styles.
@@ -377,9 +393,8 @@ namespace ImGui
// - Those functions are bound to be redesigned (they are confusing, incomplete and the Min/Max return values are in local window coordinates which increases confusion)
IMGUI_API ImVec2 GetContentRegionAvail(); // == GetContentRegionMax() - GetCursorPos()
IMGUI_API ImVec2 GetContentRegionMax(); // current content boundaries (typically window boundaries including scrolling, or current column boundaries), in windows coordinates
- IMGUI_API ImVec2 GetWindowContentRegionMin(); // content boundaries min (roughly (0,0)-Scroll), in window coordinates
- IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max (roughly (0,0)+Size-Scroll) where Size can be override with SetNextWindowContentSize(), in window coordinates
- IMGUI_API float GetWindowContentRegionWidth(); //
+ IMGUI_API ImVec2 GetWindowContentRegionMin(); // content boundaries min for the full window (roughly (0,0)-Scroll), in window coordinates
+ IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max for the full window (roughly (0,0)+Size-Scroll) where Size can be override with SetNextWindowContentSize(), in window coordinates
// Windows Scrolling
IMGUI_API float GetScrollX(); // get scrolling amount [0 .. GetScrollMaxX()]
@@ -518,12 +533,12 @@ namespace ImGui
IMGUI_API bool Combo(const char* label, int* current_item, bool(*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int popup_max_height_in_items = -1);
// Widgets: Drag Sliders
- // - CTRL+Click on any drag box to turn them into an input box. Manually input values aren't clamped and can go off-bounds.
+ // - CTRL+Click on any drag box to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp.
// - For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, note that a 'float v[X]' function argument is the same as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x
// - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc.
// - Format string may also be set to NULL or use the default format ("%f" or "%d").
// - Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For gamepad/keyboard navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision).
- // - Use v_min < v_max to clamp edits to given limits. Note that CTRL+Click manual input can override those limits.
+ // - Use v_min < v_max to clamp edits to given limits. Note that CTRL+Click manual input can override those limits if ImGuiSliderFlags_AlwaysClamp is not used.
// - Use v_max = FLT_MAX / INT_MAX etc to avoid clamping to a maximum, same with v_min = -FLT_MAX / INT_MIN to avoid clamping to a minimum.
// - We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them.
// - Legacy: Pre-1.78 there are DragXXX() function signatures that takes a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument.
@@ -542,7 +557,7 @@ namespace ImGui
IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed = 1.0f, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0);
// Widgets: Regular Sliders
- // - CTRL+Click on any slider to turn them into an input box. Manually input values aren't clamped and can go off-bounds.
+ // - CTRL+Click on any slider to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp.
// - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc.
// - Format string may also be set to NULL or use the default format ("%f" or "%d").
// - Legacy: Pre-1.78 there are SliderXXX() function signatures that takes a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument.
@@ -628,7 +643,7 @@ namespace ImGui
IMGUI_API bool ListBox(const char* label, int* current_item, bool (*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int height_in_items = -1);
// Widgets: Data Plotting
- // - Consider using ImPlot (https://github.com/epezent/implot)
+ // - Consider using ImPlot (https://github.com/epezent/implot) which is much better!
IMGUI_API void PlotLines(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float));
IMGUI_API void PlotLines(const char* label, float(*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0));
IMGUI_API void PlotHistogram(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float));
@@ -831,6 +846,7 @@ namespace ImGui
// Disabling [BETA API]
// - Disable all user interactions and dim items visuals (applying style.DisabledAlpha over current colors)
+ // - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled)
// - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it.
IMGUI_API void BeginDisabled(bool disabled = true);
IMGUI_API void EndDisabled();
@@ -1294,9 +1310,11 @@ enum ImGuiTableBgTarget_
enum ImGuiFocusedFlags_
{
ImGuiFocusedFlags_None = 0,
- ImGuiFocusedFlags_ChildWindows = 1 << 0, // IsWindowFocused(): Return true if any children of the window is focused
- ImGuiFocusedFlags_RootWindow = 1 << 1, // IsWindowFocused(): Test from root window (top most parent of the current hierarchy)
- ImGuiFocusedFlags_AnyWindow = 1 << 2, // IsWindowFocused(): Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ!
+ ImGuiFocusedFlags_ChildWindows = 1 << 0, // Return true if any children of the window is focused
+ ImGuiFocusedFlags_RootWindow = 1 << 1, // Test from root window (top most parent of the current hierarchy)
+ ImGuiFocusedFlags_AnyWindow = 1 << 2, // Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ!
+ ImGuiFocusedFlags_NoPopupHierarchy = 1 << 3, // Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow)
+ ImGuiFocusedFlags_DockHierarchy = 1 << 4, // Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow)
ImGuiFocusedFlags_RootAndChildWindows = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows
};
@@ -1309,11 +1327,13 @@ enum ImGuiHoveredFlags_
ImGuiHoveredFlags_ChildWindows = 1 << 0, // IsWindowHovered() only: Return true if any children of the window is hovered
ImGuiHoveredFlags_RootWindow = 1 << 1, // IsWindowHovered() only: Test from root window (top most parent of the current hierarchy)
ImGuiHoveredFlags_AnyWindow = 1 << 2, // IsWindowHovered() only: Return true if any window is hovered
- ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 3, // Return true even if a popup window is normally blocking access to this item/window
- //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 4, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet.
- ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 5, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns.
- ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 6, // Return true even if the position is obstructed or overlapped by another window
- ImGuiHoveredFlags_AllowWhenDisabled = 1 << 7, // Return true even if the item is disabled
+ ImGuiHoveredFlags_NoPopupHierarchy = 1 << 3, // IsWindowHovered() only: Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow)
+ ImGuiHoveredFlags_DockHierarchy = 1 << 4, // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow)
+ ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 5, // Return true even if a popup window is normally blocking access to this item/window
+ //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 6, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet.
+ ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 7, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns.
+ ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 8, // IsItemHovered() only: Return true even if the position is obstructed or overlapped by another window
+ ImGuiHoveredFlags_AllowWhenDisabled = 1 << 9, // IsItemHovered() only: Return true even if the item is disabled
ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped,
ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows
};
@@ -1329,7 +1349,7 @@ enum ImGuiDockNodeFlags_
ImGuiDockNodeFlags_NoDockingInCentralNode = 1 << 2, // Shared // Disable docking inside the Central Node, which will be always kept empty.
ImGuiDockNodeFlags_PassthruCentralNode = 1 << 3, // Shared // Enable passthru dockspace: 1) DockSpace() will render a ImGuiCol_WindowBg background covering everything excepted the Central Node when empty. Meaning the host window should probably use SetNextWindowBgAlpha(0.0f) prior to Begin() when using this. 2) When Central Node is empty: let inputs pass-through + won't display a DockingEmptyBg background. See demo for details.
ImGuiDockNodeFlags_NoSplit = 1 << 4, // Shared/Local // Disable splitting the node into smaller nodes. Useful e.g. when embedding dockspaces into a main root one (the root one may have splitting disabled to reduce confusion). Note: when turned off, existing splits will be preserved.
- ImGuiDockNodeFlags_NoResize = 1 << 5, // Shared/Local // Disable resizing node using the splitter/separators. Useful with programatically setup dockspaces.
+ ImGuiDockNodeFlags_NoResize = 1 << 5, // Shared/Local // Disable resizing node using the splitter/separators. Useful with programmatically setup dockspaces.
ImGuiDockNodeFlags_AutoHideTabBar = 1 << 6 // Shared/Local // Tab bar will automatically hide when there is a single window in the dock node.
};
@@ -1987,6 +2007,7 @@ struct ImGuiIO
// [Internal] Dear ImGui will maintain those fields. Forward compatibility not guaranteed!
//------------------------------------------------------------------
+ bool WantCaptureMouseUnlessPopupClose;// Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup.
ImGuiKeyModFlags KeyMods; // Key mods flags (same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags), updated by NewFrame()
ImGuiKeyModFlags KeyModsPrev; // Previous key mods
ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid)
@@ -1995,7 +2016,8 @@ struct ImGuiIO
bool MouseClicked[5]; // Mouse button went from !Down to Down
bool MouseDoubleClicked[5]; // Has mouse button been double-clicked?
bool MouseReleased[5]; // Mouse button went from Down to !Down
- bool MouseDownOwned[5]; // Track if button was clicked inside a dear imgui window. We don't request mouse capture from the application if click started outside ImGui bounds.
+ bool MouseDownOwned[5]; // Track if button was clicked inside a dear imgui window or over void blocked by a popup. We don't request mouse capture from the application if click started outside ImGui bounds.
+ bool MouseDownOwnedUnlessPopupClose[5];//Track if button was clicked inside a dear imgui window.
bool MouseDownWasDoubleClick[5]; // Track if button down was a double-click
float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked)
float MouseDownDurationPrev[5]; // Previous time the mouse button has been down
@@ -2382,13 +2404,6 @@ struct ImDrawCmd
inline ImTextureID GetTexID() const { return TextureId; }
};
-// Vertex index, default to 16-bit
-// To allow large meshes with 16-bit indices: set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset' and handle ImDrawCmd::VtxOffset in the renderer backend (recommended).
-// To use 32-bit indices: override with '#define ImDrawIdx unsigned int' in imconfig.h.
-#ifndef ImDrawIdx
-typedef unsigned short ImDrawIdx;
-#endif
-
// Vertex layout
#ifndef IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT
struct ImDrawVert
@@ -3067,6 +3082,8 @@ struct ImGuiPlatformMonitor
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
namespace ImGui
{
+ // OBSOLETED in 1.85 (from August 2021)
+ static inline float GetWindowContentRegionWidth() { return GetWindowContentRegionMax().x - GetWindowContentRegionMin().x; }
// OBSOLETED in 1.81 (from February 2021)
IMGUI_API bool ListBoxHeader(const char* label, int items_count, int height_in_items = -1); // Helper to calculate size from items_count and height_in_items
static inline bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0, 0)) { return BeginListBox(label, size); }
diff --git a/imgui/imgui_demo.cpp b/imgui/imgui_demo.cpp
index 10f72839..1ee3d7cc 100644
--- a/imgui/imgui_demo.cpp
+++ b/imgui/imgui_demo.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.84
+// dear imgui, v1.85
// (demo code)
// Help:
@@ -308,10 +308,12 @@ void ImGui::ShowDemoWindow(bool* p_open)
// Dear ImGui Apps (accessible from the "Tools" menu)
static bool show_app_metrics = false;
+ static bool show_app_stack_tool = false;
static bool show_app_style_editor = false;
static bool show_app_about = false;
if (show_app_metrics) { ImGui::ShowMetricsWindow(&show_app_metrics); }
+ if (show_app_stack_tool) { ImGui::ShowStackToolWindow(&show_app_stack_tool); }
if (show_app_about) { ImGui::ShowAboutWindow(&show_app_about); }
if (show_app_style_editor)
{
@@ -396,9 +398,13 @@ void ImGui::ShowDemoWindow(bool* p_open)
ImGui::MenuItem("Documents", NULL, &show_app_documents);
ImGui::EndMenu();
}
+ //if (ImGui::MenuItem("MenuItem")) {} // You can also use MenuItem() inside a menu bar!
if (ImGui::BeginMenu("Tools"))
{
+#ifndef IMGUI_DISABLE_METRICS_WINDOW
ImGui::MenuItem("Metrics/Debugger", NULL, &show_app_metrics);
+ ImGui::MenuItem("Stack Tool", NULL, &show_app_stack_tool);
+#endif
ImGui::MenuItem("Style Editor", NULL, &show_app_style_editor);
ImGui::MenuItem("About Dear ImGui", NULL, &show_app_about);
ImGui::EndMenu();
@@ -1613,16 +1619,17 @@ static void ShowDemoWindowWidgets()
}
// Plot/Graph widgets are not very good.
- // Consider writing your own, or using a third-party one, see:
- // - ImPlot https://github.com/epezent/implot
- // - others https://github.com/ocornut/imgui/wiki/Useful-Extensions
+ // Consider using a third-party library such as ImPlot: https://github.com/epezent/implot
+ // (see others https://github.com/ocornut/imgui/wiki/Useful-Extensions)
if (ImGui::TreeNode("Plots Widgets"))
{
static bool animate = true;
ImGui::Checkbox("Animate", &animate);
+ // Plot as lines and plot as histogram
static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f };
ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr));
+ ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0, 80.0f));
// Fill an array of contiguous float values to plot
// Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float
@@ -1652,7 +1659,6 @@ static void ShowDemoWindowWidgets()
sprintf(overlay, "avg %f", average);
ImGui::PlotLines("Lines", values, IM_ARRAYSIZE(values), values_offset, overlay, -1.0f, 1.0f, ImVec2(0, 80.0f));
}
- ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0, 80.0f));
// Use functions to generate output
// FIXME: This is rather awkward because current plot API only pass in indices.
@@ -2237,7 +2243,7 @@ static void ShowDemoWindowWidgets()
ImGui::TreePop();
}
- if (ImGui::TreeNode("Querying Status (Edited/Active/Hovered etc.)"))
+ if (ImGui::TreeNode("Querying Item Status (Edited/Active/Hovered etc.)"))
{
// Select an item type
const char* item_names[] =
@@ -2323,43 +2329,75 @@ static void ShowDemoWindowWidgets()
if (item_disabled)
ImGui::EndDisabled();
+ char buf[1] = "";
+ ImGui::InputText("unused", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_ReadOnly);
+ ImGui::SameLine();
+ HelpMarker("This widget is only here to be able to tab-out of the widgets above and see e.g. Deactivated() status.");
+
+ ImGui::TreePop();
+ }
+
+ if (ImGui::TreeNode("Querying Window Status (Focused/Hovered etc.)"))
+ {
static bool embed_all_inside_a_child_window = false;
- ImGui::Checkbox("Embed everything inside a child window (for additional testing)", &embed_all_inside_a_child_window);
+ ImGui::Checkbox("Embed everything inside a child window for testing _RootWindow flag.", &embed_all_inside_a_child_window);
if (embed_all_inside_a_child_window)
ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20.0f), true);
// Testing IsWindowFocused() function with its various flags.
- // Note that the ImGuiFocusedFlags_XXX flags can be combined.
ImGui::BulletText(
"IsWindowFocused() = %d\n"
"IsWindowFocused(_ChildWindows) = %d\n"
+ "IsWindowFocused(_ChildWindows|_NoPopupHierarchy) = %d\n"
+ "IsWindowFocused(_ChildWindows|_DockHierarchy) = %d\n"
"IsWindowFocused(_ChildWindows|_RootWindow) = %d\n"
+ "IsWindowFocused(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n"
+ "IsWindowFocused(_ChildWindows|_RootWindow|_DockHierarchy) = %d\n"
"IsWindowFocused(_RootWindow) = %d\n"
+ "IsWindowFocused(_RootWindow|_NoPopupHierarchy) = %d\n"
+ "IsWindowFocused(_RootWindow|_DockHierarchy) = %d\n"
"IsWindowFocused(_AnyWindow) = %d\n",
ImGui::IsWindowFocused(),
ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows),
+ ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_NoPopupHierarchy),
+ ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_DockHierarchy),
ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow),
+ ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy),
+ ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy),
ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow),
+ ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy),
+ ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy),
ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow));
// Testing IsWindowHovered() function with its various flags.
- // Note that the ImGuiHoveredFlags_XXX flags can be combined.
ImGui::BulletText(
"IsWindowHovered() = %d\n"
"IsWindowHovered(_AllowWhenBlockedByPopup) = %d\n"
"IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n"
"IsWindowHovered(_ChildWindows) = %d\n"
+ "IsWindowHovered(_ChildWindows|_NoPopupHierarchy) = %d\n"
+ "IsWindowHovered(_ChildWindows|_DockHierarchy) = %d\n"
"IsWindowHovered(_ChildWindows|_RootWindow) = %d\n"
- "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n"
+ "IsWindowHovered(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n"
+ "IsWindowHovered(_ChildWindows|_RootWindow|_DockHierarchy) = %d\n"
"IsWindowHovered(_RootWindow) = %d\n"
+ "IsWindowHovered(_RootWindow|_NoPopupHierarchy) = %d\n"
+ "IsWindowHovered(_RootWindow|_DockHierarchy) = %d\n"
+ "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n"
"IsWindowHovered(_AnyWindow) = %d\n",
ImGui::IsWindowHovered(),
ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup),
ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),
ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows),
+ ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy),
+ ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_DockHierarchy),
ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow),
- ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup),
+ ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy),
+ ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_DockHierarchy),
ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow),
+ ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy),
+ ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_DockHierarchy),
+ ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup),
ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow));
ImGui::BeginChild("child", ImVec2(0, 50), true);
@@ -2368,9 +2406,6 @@ static void ShowDemoWindowWidgets()
if (embed_all_inside_a_child_window)
ImGui::EndChild();
- static char unused_str[] = "This widget is only here to be able to tab-out of the widgets above.";
- ImGui::InputText("unused", unused_str, IM_ARRAYSIZE(unused_str), ImGuiInputTextFlags_ReadOnly);
-
// Calling IsItemHovered() after begin returns the hovered status of the title bar.
// This is useful in particular if you want to create a context menu associated to the title bar of a window.
// This will also work when docked into a Tab (the Tab replace the Title Bar and guarantee the same properties).
@@ -2427,7 +2462,7 @@ static void ShowDemoWindowLayout()
ImGuiWindowFlags window_flags = ImGuiWindowFlags_HorizontalScrollbar;
if (disable_mouse_wheel)
window_flags |= ImGuiWindowFlags_NoScrollWithMouse;
- ImGui::BeginChild("ChildL", ImVec2(ImGui::GetWindowContentRegionWidth() * 0.5f, 260), false, window_flags);
+ ImGui::BeginChild("ChildL", ImVec2(ImGui::GetContentRegionAvail().x * 0.5f, 260), false, window_flags);
for (int i = 0; i < 100; i++)
ImGui::Text("%04d: scrollable region", i);
ImGui::EndChild();
@@ -5536,6 +5571,7 @@ static void ShowDemoWindowMisc()
// Display ImGuiIO output flags
ImGui::Text("WantCaptureMouse: %d", io.WantCaptureMouse);
+ ImGui::Text("WantCaptureMouseUnlessPopupClose: %d", io.WantCaptureMouseUnlessPopupClose);
ImGui::Text("WantCaptureKeyboard: %d", io.WantCaptureKeyboard);
ImGui::Text("WantTextInput: %d", io.WantTextInput);
ImGui::Text("WantSetMousePos: %d", io.WantSetMousePos);
diff --git a/imgui/imgui_draw.cpp b/imgui/imgui_draw.cpp
index dbcf57fd..0429a864 100644
--- a/imgui/imgui_draw.cpp
+++ b/imgui/imgui_draw.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.84
+// dear imgui, v1.85
// (drawing and font code)
/*
@@ -2004,7 +2004,7 @@ void ImFontAtlas::ClearInputData()
ConfigData.clear();
CustomRects.clear();
PackIdMouseCursors = PackIdLines = -1;
- TexReady = false;
+ // Important: we leave TexReady untouched
}
void ImFontAtlas::ClearTexData()
diff --git a/imgui/imgui_internal.h b/imgui/imgui_internal.h
index 28a5b458..45bbfbd2 100644
--- a/imgui/imgui_internal.h
+++ b/imgui/imgui_internal.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.84
+// dear imgui, v1.85
// (internal structures/api)
// You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility!
@@ -18,12 +18,13 @@ Index of this file:
// [SECTION] Generic helpers
// [SECTION] ImDrawList support
// [SECTION] Widgets support: flags, enums, data structures
+// [SECTION] Navigation support
// [SECTION] Columns support
// [SECTION] Multi-select support
// [SECTION] Docking support
// [SECTION] Viewport support
// [SECTION] Settings support
-// [SECTION] Metrics, Debug
+// [SECTION] Metrics, Debug tools
// [SECTION] Generic context hooks
// [SECTION] ImGuiContext (main imgui context)
// [SECTION] ImGuiWindowTempData, ImGuiWindow
@@ -82,19 +83,13 @@ Index of this file:
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
#pragma clang diagnostic ignored "-Wdouble-promotion"
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
+#pragma clang diagnostic ignored "-Wmissing-noreturn" // warning: function 'xxx' could be declared with attribute 'noreturn'
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
#endif
-// Helper macros
-#if defined(__clang__)
-#define IM_NORETURN __attribute__((noreturn))
-#else
-#define IM_NORETURN
-#endif
-
// Legacy defines
#ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS // Renamed in 1.74
#error Use IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
@@ -153,8 +148,8 @@ struct ImGuiWindowSettings; // Storage for a window .ini settings (we ke
// Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists.
typedef int ImGuiDataAuthority; // -> enum ImGuiDataAuthority_ // Enum: for storing the source authority (dock node vs window) of a field
typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical
+typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later)
typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag()
-typedef int ImGuiItemAddFlags; // -> enum ImGuiItemAddFlags_ // Flags: for ItemAdd()
typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for DC.LastItemStatusFlags
typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns()
typedef int ImGuiNavHighlightFlags; // -> enum ImGuiNavHighlightFlags_ // Flags: for RenderNavHighlight()
@@ -162,6 +157,7 @@ typedef int ImGuiNavDirSourceFlags; // -> enum ImGuiNavDirSourceFlags_ // F
typedef int ImGuiNavMoveFlags; // -> enum ImGuiNavMoveFlags_ // Flags: for navigation requests
typedef int ImGuiNextItemDataFlags; // -> enum ImGuiNextItemDataFlags_ // Flags: for SetNextItemXXX() functions
typedef int ImGuiNextWindowDataFlags; // -> enum ImGuiNextWindowDataFlags_// Flags: for SetNextWindowXXX() functions
+typedef int ImGuiScrollFlags; // -> enum ImGuiScrollFlags_ // Flags: for ScrollToItem() and navigation requests
typedef int ImGuiSeparatorFlags; // -> enum ImGuiSeparatorFlags_ // Flags: for SeparatorEx()
typedef int ImGuiTextFlags; // -> enum ImGuiTextFlags_ // Flags: for TextEx()
typedef int ImGuiTooltipFlags; // -> enum ImGuiTooltipFlags_ // Flags: for BeginTooltipEx()
@@ -759,15 +755,8 @@ enum ImGuiItemFlags_
ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false // Disable item being a candidate for default focus (e.g. used by title bar items)
ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // Disable MenuItem/Selectable() automatically closing their popup window
ImGuiItemFlags_MixedValue = 1 << 6, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets)
- ImGuiItemFlags_ReadOnly = 1 << 7 // false // [ALPHA] Allow hovering interactions but underlying value is not changed.
-};
-
-// Flags for ItemAdd()
-// FIXME-NAV: _Focusable is _ALMOST_ what you would expect to be called '_TabStop' but because SetKeyboardFocusHere() works on items with no TabStop we distinguish Focusable from TabStop.
-enum ImGuiItemAddFlags_
-{
- ImGuiItemAddFlags_None = 0,
- ImGuiItemAddFlags_Focusable = 1 << 0 // FIXME-NAV: In current/legacy scheme, Focusable+TabStop support are opt-in by widgets. We will transition it toward being opt-out, so this flag is expected to eventually disappear.
+ ImGuiItemFlags_ReadOnly = 1 << 7, // false // [ALPHA] Allow hovering interactions but underlying value is not changed.
+ ImGuiItemFlags_Inputable = 1 << 8 // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature.
};
// Storage for LastItem data
@@ -775,16 +764,14 @@ enum ImGuiItemStatusFlags_
{
ImGuiItemStatusFlags_None = 0,
ImGuiItemStatusFlags_HoveredRect = 1 << 0, // Mouse position is within item rectangle (does NOT mean that the window is in correct z-order and can be hovered!, this is only one part of the most-common IsItemHovered test)
- ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, // window->DC.LastItemDisplayRect is valid
+ ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, // g.LastItemData.DisplayRect is valid
ImGuiItemStatusFlags_Edited = 1 << 2, // Value exposed by item was edited in the current frame (should match the bool return value of most widgets)
ImGuiItemStatusFlags_ToggledSelection = 1 << 3, // Set when Selectable(), TreeNode() reports toggling a selection. We can't report "Selected", only state changes, in order to easily handle clipping with less issues.
ImGuiItemStatusFlags_ToggledOpen = 1 << 4, // Set when TreeNode() reports toggling their open state.
ImGuiItemStatusFlags_HasDeactivated = 1 << 5, // Set if the widget/group is able to provide data for the ImGuiItemStatusFlags_Deactivated flag.
ImGuiItemStatusFlags_Deactivated = 1 << 6, // Only valid if ImGuiItemStatusFlags_HasDeactivated is set.
ImGuiItemStatusFlags_HoveredWindow = 1 << 7, // Override the HoveredWindow test to allow cross-window hover testing.
- ImGuiItemStatusFlags_FocusedByCode = 1 << 8, // Set when the Focusable item just got focused from code.
- ImGuiItemStatusFlags_FocusedByTabbing = 1 << 9, // Set when the Focusable item just got focused by Tabbing.
- ImGuiItemStatusFlags_Focused = ImGuiItemStatusFlags_FocusedByCode | ImGuiItemStatusFlags_FocusedByTabbing
+ ImGuiItemStatusFlags_FocusedByTabbing = 1 << 8 // Set when the Focusable item just got focused by Tabbing (FIXME: to be removed soon)
#ifdef IMGUI_ENABLE_TEST_ENGINE
, // [imgui_tests only]
@@ -933,49 +920,6 @@ enum ImGuiInputReadMode
ImGuiInputReadMode_RepeatFast
};
-enum ImGuiNavHighlightFlags_
-{
- ImGuiNavHighlightFlags_None = 0,
- ImGuiNavHighlightFlags_TypeDefault = 1 << 0,
- ImGuiNavHighlightFlags_TypeThin = 1 << 1,
- ImGuiNavHighlightFlags_AlwaysDraw = 1 << 2, // Draw rectangular highlight if (g.NavId == id) _even_ when using the mouse.
- ImGuiNavHighlightFlags_NoRounding = 1 << 3
-};
-
-enum ImGuiNavDirSourceFlags_
-{
- ImGuiNavDirSourceFlags_None = 0,
- ImGuiNavDirSourceFlags_Keyboard = 1 << 0,
- ImGuiNavDirSourceFlags_PadDPad = 1 << 1,
- ImGuiNavDirSourceFlags_PadLStick = 1 << 2
-};
-
-enum ImGuiNavMoveFlags_
-{
- ImGuiNavMoveFlags_None = 0,
- ImGuiNavMoveFlags_LoopX = 1 << 0, // On failed request, restart from opposite side
- ImGuiNavMoveFlags_LoopY = 1 << 1,
- ImGuiNavMoveFlags_WrapX = 1 << 2, // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left)
- ImGuiNavMoveFlags_WrapY = 1 << 3, // This is not super useful for provided for completeness
- ImGuiNavMoveFlags_AllowCurrentNavId = 1 << 4, // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place)
- ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisibleSet that only comprise elements that are already fully visible.
- ImGuiNavMoveFlags_ScrollToEdge = 1 << 6
-};
-
-enum ImGuiNavForward
-{
- ImGuiNavForward_None,
- ImGuiNavForward_ForwardQueued,
- ImGuiNavForward_ForwardActive
-};
-
-enum ImGuiNavLayer
-{
- ImGuiNavLayer_Main = 0, // Main scrolling layer
- ImGuiNavLayer_Menu = 1, // Menu layer (access with Alt/ImGuiNavInput_Menu)
- ImGuiNavLayer_COUNT
-};
-
enum ImGuiPopupPositionPolicy
{
ImGuiPopupPositionPolicy_Default,
@@ -1122,20 +1066,6 @@ struct ImGuiPopupData
ImGuiPopupData() { memset(this, 0, sizeof(*this)); OpenFrameCount = -1; }
};
-struct ImGuiNavItemData
-{
- ImGuiWindow* Window; // Init,Move // Best candidate window (result->ItemWindow->RootWindowForNav == request->Window)
- ImGuiID ID; // Init,Move // Best candidate item ID
- ImGuiID FocusScopeId; // Init,Move // Best candidate focus scope ID
- ImRect RectRel; // Init,Move // Best candidate bounding box in window relative space
- float DistBox; // Move // Best candidate box distance to current NavId
- float DistCenter; // Move // Best candidate center distance to current NavId
- float DistAxial; // Move // Best candidate axial distance to current NavId
-
- ImGuiNavItemData() { Clear(); }
- void Clear() { Window = NULL; ID = FocusScopeId = 0; RectRel = ImRect(); DistBox = DistCenter = DistAxial = FLT_MAX; }
-};
-
enum ImGuiNextWindowDataFlags_
{
ImGuiNextWindowDataFlags_None = 0,
@@ -1205,17 +1135,36 @@ struct ImGuiLastItemData
ImGuiID ID;
ImGuiItemFlags InFlags; // See ImGuiItemFlags_
ImGuiItemStatusFlags StatusFlags; // See ImGuiItemStatusFlags_
- ImRect Rect;
- ImRect DisplayRect;
+ ImRect Rect; // Full rectangle
+ ImRect NavRect; // Navigation scoring rectangle (not displayed)
+ ImRect DisplayRect; // Display rectangle (only if ImGuiItemStatusFlags_HasDisplayRect is set)
ImGuiLastItemData() { memset(this, 0, sizeof(*this)); }
};
+struct IMGUI_API ImGuiStackSizes
+{
+ short SizeOfIDStack;
+ short SizeOfColorStack;
+ short SizeOfStyleVarStack;
+ short SizeOfFontStack;
+ short SizeOfFocusScopeStack;
+ short SizeOfGroupStack;
+ short SizeOfItemFlagsStack;
+ short SizeOfBeginPopupStack;
+ short SizeOfDisabledStack;
+
+ ImGuiStackSizes() { memset(this, 0, sizeof(*this)); }
+ void SetToCurrentState();
+ void CompareWithCurrentState();
+};
+
// Data saved for each window pushed into the stack
struct ImGuiWindowStackData
{
ImGuiWindow* Window;
ImGuiLastItemData ParentLastItemDataBackup;
+ ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting
};
struct ImGuiShrinkWidthItem
@@ -1234,6 +1183,89 @@ struct ImGuiPtrOrIndex
};
//-----------------------------------------------------------------------------
+// [SECTION] Navigation support
+//-----------------------------------------------------------------------------
+
+enum ImGuiActivateFlags_
+{
+ ImGuiActivateFlags_None = 0,
+ ImGuiActivateFlags_PreferInput = 1 << 0, // Favor activation that requires keyboard text input (e.g. for Slider/Drag). Default if keyboard is available.
+ ImGuiActivateFlags_PreferTweak = 1 << 1, // Favor activation for tweaking with arrows or gamepad (e.g. for Slider/Drag). Default if keyboard is not available.
+ ImGuiActivateFlags_TryToPreserveState = 1 << 2 // Request widget to preserve state if it can (e.g. InputText will try to preserve cursor/selection)
+};
+
+// Early work-in-progress API for ScrollToItem()
+enum ImGuiScrollFlags_
+{
+ ImGuiScrollFlags_None = 0,
+ ImGuiScrollFlags_KeepVisibleEdgeX = 1 << 0, // If item is not visible: scroll as little as possible on X axis to bring item back into view [default for X axis]
+ ImGuiScrollFlags_KeepVisibleEdgeY = 1 << 1, // If item is not visible: scroll as little as possible on Y axis to bring item back into view [default for Y axis for windows that are already visible]
+ ImGuiScrollFlags_KeepVisibleCenterX = 1 << 2, // If item is not visible: scroll to make the item centered on X axis [rarely used]
+ ImGuiScrollFlags_KeepVisibleCenterY = 1 << 3, // If item is not visible: scroll to make the item centered on Y axis
+ ImGuiScrollFlags_AlwaysCenterX = 1 << 4, // Always center the result item on X axis [rarely used]
+ ImGuiScrollFlags_AlwaysCenterY = 1 << 5, // Always center the result item on Y axis [default for Y axis for appearing window)
+ ImGuiScrollFlags_NoScrollParent = 1 << 6, // Disable forwarding scrolling to parent window if required to keep item/rect visible (only scroll window the function was applied to).
+ ImGuiScrollFlags_MaskX_ = ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleCenterX | ImGuiScrollFlags_AlwaysCenterX,
+ ImGuiScrollFlags_MaskY_ = ImGuiScrollFlags_KeepVisibleEdgeY | ImGuiScrollFlags_KeepVisibleCenterY | ImGuiScrollFlags_AlwaysCenterY
+};
+
+enum ImGuiNavHighlightFlags_
+{
+ ImGuiNavHighlightFlags_None = 0,
+ ImGuiNavHighlightFlags_TypeDefault = 1 << 0,
+ ImGuiNavHighlightFlags_TypeThin = 1 << 1,
+ ImGuiNavHighlightFlags_AlwaysDraw = 1 << 2, // Draw rectangular highlight if (g.NavId == id) _even_ when using the mouse.
+ ImGuiNavHighlightFlags_NoRounding = 1 << 3
+};
+
+enum ImGuiNavDirSourceFlags_
+{
+ ImGuiNavDirSourceFlags_None = 0,
+ ImGuiNavDirSourceFlags_Keyboard = 1 << 0,
+ ImGuiNavDirSourceFlags_PadDPad = 1 << 1,
+ ImGuiNavDirSourceFlags_PadLStick = 1 << 2
+};
+
+enum ImGuiNavMoveFlags_
+{
+ ImGuiNavMoveFlags_None = 0,
+ ImGuiNavMoveFlags_LoopX = 1 << 0, // On failed request, restart from opposite side
+ ImGuiNavMoveFlags_LoopY = 1 << 1,
+ ImGuiNavMoveFlags_WrapX = 1 << 2, // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left)
+ ImGuiNavMoveFlags_WrapY = 1 << 3, // This is not super useful but provided for completeness
+ ImGuiNavMoveFlags_AllowCurrentNavId = 1 << 4, // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place)
+ ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisible that only comprise elements that are already fully visible (used by PageUp/PageDown)
+ ImGuiNavMoveFlags_ScrollToEdgeY = 1 << 6, // Force scrolling to min/max (used by Home/End) // FIXME-NAV: Aim to remove or reword, probably unnecessary
+ ImGuiNavMoveFlags_Forwarded = 1 << 7,
+ ImGuiNavMoveFlags_DebugNoResult = 1 << 8, // Dummy scoring for debug purpose, don't apply result
+ ImGuiNavMoveFlags_Tabbing = 1 << 9, // == Focus + Activate if item is Inputable + DontChangeNavHighlight
+ ImGuiNavMoveFlags_Activate = 1 << 10,
+ ImGuiNavMoveFlags_DontSetNavHighlight = 1 << 11 // Do not alter the visible state of keyboard vs mouse nav highlight
+};
+
+enum ImGuiNavLayer
+{
+ ImGuiNavLayer_Main = 0, // Main scrolling layer
+ ImGuiNavLayer_Menu = 1, // Menu layer (access with Alt/ImGuiNavInput_Menu)
+ ImGuiNavLayer_COUNT
+};
+
+struct ImGuiNavItemData
+{
+ ImGuiWindow* Window; // Init,Move // Best candidate window (result->ItemWindow->RootWindowForNav == request->Window)
+ ImGuiID ID; // Init,Move // Best candidate item ID
+ ImGuiID FocusScopeId; // Init,Move // Best candidate focus scope ID
+ ImRect RectRel; // Init,Move // Best candidate bounding box in window relative space
+ ImGuiItemFlags InFlags; // ????,Move // Best candidate item flags
+ float DistBox; // Move // Best candidate box distance to current NavId
+ float DistCenter; // Move // Best candidate center distance to current NavId
+ float DistAxial; // Move // Best candidate axial distance to current NavId
+
+ ImGuiNavItemData() { Clear(); }
+ void Clear() { Window = NULL; ID = FocusScopeId = 0; InFlags = 0; DistBox = DistCenter = DistAxial = FLT_MAX; }
+};
+
+//-----------------------------------------------------------------------------
// [SECTION] Columns support
//-----------------------------------------------------------------------------
@@ -1367,6 +1399,7 @@ struct IMGUI_API ImGuiDockNode
ImGuiWindow* VisibleWindow; // Generally point to window which is ID is == SelectedTabID, but when CTRL+Tabbing this can be a different window.
ImGuiDockNode* CentralNode; // [Root node only] Pointer to central node.
ImGuiDockNode* OnlyNodeWithWindows; // [Root node only] Set when there is a single visible node within the hierarchy.
+ int CountNodeWithWindows; // [Root node only]
int LastFrameAlive; // Last frame number the node was updated or kept alive explicitly with DockSpace() + ImGuiDockNodeFlags_KeepAliveOnly
int LastFrameActive; // Last frame number the node was updated.
int LastFrameFocused; // Last frame number the node was focused.
@@ -1380,6 +1413,7 @@ struct IMGUI_API ImGuiDockNode
bool IsFocused :1;
bool HasCloseButton :1; // Provide space for a close button (if any of the docked window has one). Note that button may be hidden on window without one.
bool HasWindowMenuButton :1;
+ bool HasCentralNodeChild :1;
bool WantCloseAll :1; // Set when closing all tabs at once.
bool WantLockSizeOnce :1;
bool WantMouseMove :1; // After a node extraction we need to transition toward moving the newly created host window
@@ -1520,11 +1554,12 @@ struct ImGuiSettingsHandler
};
//-----------------------------------------------------------------------------
-// [SECTION] Metrics, Debug
+// [SECTION] Metrics, Debug Tools
//-----------------------------------------------------------------------------
struct ImGuiMetricsConfig
{
+ bool ShowStackTool;
bool ShowWindowsRects;
bool ShowWindowsBeginOrder;
bool ShowTablesRects;
@@ -1536,6 +1571,7 @@ struct ImGuiMetricsConfig
ImGuiMetricsConfig()
{
+ ShowStackTool = false;
ShowWindowsRects = false;
ShowWindowsBeginOrder = false;
ShowTablesRects = false;
@@ -1547,19 +1583,25 @@ struct ImGuiMetricsConfig
}
};
-struct IMGUI_API ImGuiStackSizes
+struct ImGuiStackLevelInfo
{
- short SizeOfIDStack;
- short SizeOfColorStack;
- short SizeOfStyleVarStack;
- short SizeOfFontStack;
- short SizeOfFocusScopeStack;
- short SizeOfGroupStack;
- short SizeOfBeginPopupStack;
+ ImGuiID ID;
+ ImS8 QueryFrameCount; // >= 1: Query in progress
+ bool QuerySuccess; // Obtained result from DebugHookIdInfo()
+ char Desc[58]; // Arbitrarily sized buffer to hold a result (FIXME: could replace Results[] with a chunk stream?)
- ImGuiStackSizes() { memset(this, 0, sizeof(*this)); }
- void SetToCurrentState();
- void CompareWithCurrentState();
+ ImGuiStackLevelInfo() { memset(this, 0, sizeof(*this)); }
+};
+
+// State for Stack tool queries
+struct ImGuiStackTool
+{
+ int LastActiveFrame;
+ int StackLevel; // -1: query stack and resize Results, >= 0: individual stack level
+ ImGuiID QueryId; // ID to query details for
+ ImVector<ImGuiStackLevelInfo> Results;
+
+ ImGuiStackTool() { memset(this, 0, sizeof(*this)); }
};
//-----------------------------------------------------------------------------
@@ -1607,7 +1649,6 @@ struct ImGuiContext
bool WithinEndChild; // Set within EndChild()
bool GcCompactAll; // Request full GC
bool TestEngineHookItems; // Will call test engine hooks: ImGuiTestEngineHook_ItemAdd(), ImGuiTestEngineHook_ItemInfo(), ImGuiTestEngineHook_Log()
- ImGuiID TestEngineHookIdInfo; // Will call test engine hooks: ImGuiTestEngineHook_IdInfo() from GetID()
void* TestEngine; // Test engine user data
// Windows state
@@ -1621,13 +1662,14 @@ struct ImGuiContext
ImGuiWindow* CurrentWindow; // Window being drawn into
ImGuiWindow* HoveredWindow; // Window the mouse is hovering. Will typically catch mouse inputs.
ImGuiWindow* HoveredWindowUnderMovingWindow; // Hovered window ignoring MovingWindow. Only set if MovingWindow is set.
- ImGuiDockNode* HoveredDockNode; // Hovered dock node.
+ ImGuiDockNode* HoveredDockNode; // [Debug] Hovered dock node.
ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow->RootWindowDockTree.
ImGuiWindow* WheelingWindow; // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window.
ImVec2 WheelingWindowRefMousePos;
float WheelingWindowTimer;
// Item/widgets state and tracking information
+ ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line]
ImGuiID HoveredId; // Hovered widget, filled during the frame
ImGuiID HoveredIdPreviousFrame;
bool HoveredIdAllowOverlap;
@@ -1693,37 +1735,43 @@ struct ImGuiContext
ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0, also set when calling ActivateItem()
ImGuiID NavActivateDownId; // ~~ IsNavInputDown(ImGuiNavInput_Activate) ? NavId : 0
ImGuiID NavActivatePressedId; // ~~ IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0
- ImGuiID NavInputId; // ~~ IsNavInputPressed(ImGuiNavInput_Input) ? NavId : 0
+ ImGuiID NavActivateInputId; // ~~ IsNavInputPressed(ImGuiNavInput_Input) ? NavId : 0; ImGuiActivateFlags_PreferInput will be set and NavActivateId will be 0.
+ ImGuiActivateFlags NavActivateFlags;
ImGuiID NavJustTabbedId; // Just tabbed to this id.
ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest).
ImGuiID NavJustMovedToFocusScopeId; // Just navigated to this focus scope id (result of a successfully MoveRequest).
ImGuiKeyModFlags NavJustMovedToKeyMods;
ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame.
+ ImGuiActivateFlags NavNextActivateFlags;
ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS WILL ONLY BE None or NavGamepad or NavKeyboard.
- ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring.
- int NavScoringCount; // Metrics for debugging
ImGuiNavLayer NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later.
int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing
bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRectRel is valid
bool NavMousePosDirty; // When set we will update mouse position if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) if set (NB: this not enabled by default)
bool NavDisableHighlight; // When user starts using mouse, we hide gamepad/keyboard highlight (NB: but they are still available, which is why NavDisableHighlight isn't always != NavDisableMouseHover)
bool NavDisableMouseHover; // When user starts using gamepad/keyboard, we hide mouse hovering highlight until mouse is touched again.
- bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest
+
+ // Navigation: Init & Move Requests
+ bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest this is to perform early out in ItemAdd()
bool NavInitRequest; // Init request for appearing window to select first item
bool NavInitRequestFromMove;
ImGuiID NavInitResultId; // Init request result (first item of the window, or one for which SetItemDefaultFocus() was called)
ImRect NavInitResultRectRel; // Init request result rectangle (relative to parent window)
- bool NavMoveRequest; // Move request for this frame
- ImGuiNavMoveFlags NavMoveRequestFlags;
- ImGuiNavForward NavMoveRequestForward; // None / ForwardQueued / ForwardActive (this is used to navigate sibling parent menus from a child menu)
- ImGuiKeyModFlags NavMoveRequestKeyMods;
- ImGuiDir NavMoveDir, NavMoveDirLast; // Direction of the move request (left/right/up/down), direction of the previous move request
+ bool NavMoveSubmitted; // Move request submitted, will process result on next NewFrame()
+ bool NavMoveScoringItems; // Move request submitted, still scoring incoming items
+ bool NavMoveForwardToNextFrame;
+ ImGuiNavMoveFlags NavMoveFlags;
+ ImGuiScrollFlags NavMoveScrollFlags;
+ ImGuiKeyModFlags NavMoveKeyMods;
+ ImGuiDir NavMoveDir; // Direction of the move request (left/right/up/down)
+ ImGuiDir NavMoveDirForDebug;
ImGuiDir NavMoveClipDir; // FIXME-NAV: Describe the purpose of this better. Might want to rename?
+ ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring.
+ int NavScoringDebugCount; // Metrics for debugging
+ int NavTabbingInputableRemaining; // >0 when counting items for tabbing
ImGuiNavItemData NavMoveResultLocal; // Best move request candidate within NavWindow
- ImGuiNavItemData NavMoveResultLocalVisibleSet; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag)
+ ImGuiNavItemData NavMoveResultLocalVisible; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag)
ImGuiNavItemData NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag)
- ImGuiWindow* NavWrapRequestWindow; // Window which requested trying nav wrap-around.
- ImGuiNavMoveFlags NavWrapRequestFlags; // Wrap-around operation flags.
// Navigation: Windowing (CTRL+TAB for list, or Menu button + keys or directional pads to move/resize)
ImGuiWindow* NavWindowingTarget; // Target window when doing CTRL+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most!
@@ -1736,9 +1784,7 @@ struct ImGuiContext
// Legacy Focus/Tabbing system (older than Nav, active even if Nav is disabled, misnamed. FIXME-NAV: This needs a redesign!)
ImGuiWindow* TabFocusRequestCurrWindow; //
ImGuiWindow* TabFocusRequestNextWindow; //
- int TabFocusRequestCurrCounterRegular; // Any item being requested for focus, stored as an index (we on layout to be stable between the frame pressing TAB and the next frame, semi-ouch)
int TabFocusRequestCurrCounterTabStop; // Tab item being requested for focus, stored as an index
- int TabFocusRequestNextCounterRegular; // Stored for next frame
int TabFocusRequestNextCounterTabStop; // "
bool TabFocusPressed; // Set in NewFrame() when user pressed Tab
@@ -1780,14 +1826,14 @@ struct ImGuiContext
ImVector<ImGuiShrinkWidthItem> ShrinkWidthBuffer;
// Widget state
- ImVec2 LastValidMousePos;
+ ImVec2 MouseLastValidPos;
ImGuiInputTextState InputTextState;
ImFont InputTextPasswordFont;
ImGuiID TempInputId; // Temporary text input when CTRL+clicking on a slider, etc.
ImGuiColorEditFlags ColorEditOptions; // Store user options for color edit widgets
- float ColorEditLastHue; // Backup of last Hue associated to LastColor[3], so we can restore Hue in lossy RGB<>HSV round trips
- float ColorEditLastSat; // Backup of last Saturation associated to LastColor[3], so we can restore Saturation in lossy RGB<>HSV round trips
- float ColorEditLastColor[3];
+ float ColorEditLastHue; // Backup of last Hue associated to LastColor, so we can restore Hue in lossy RGB<>HSV round trips
+ float ColorEditLastSat; // Backup of last Saturation associated to LastColor, so we can restore Saturation in lossy RGB<>HSV round trips
+ ImU32 ColorEditLastColor; // RGB value with alpha set to 0.
ImVec4 ColorPickerRef; // Initial/reference color at the time of opening the color picker.
ImGuiComboPreviewData ComboPreviewData;
float SliderCurrentAccum; // Accumulated slider delta when using navigation controls.
@@ -1795,9 +1841,10 @@ struct ImGuiContext
bool DragCurrentAccumDirty;
float DragCurrentAccum; // Accumulator for dragging modification. Always high-precision, not rounded by end-user precision settings
float DragSpeedDefaultRatio; // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio
- float DisabledAlphaBackup; // Backup for style.Alpha for BeginDisabled()
float ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage?
- int TooltipOverrideCount;
+ float DisabledAlphaBackup; // Backup for style.Alpha for BeginDisabled()
+ short DisabledStackSize;
+ short TooltipOverrideCount;
float TooltipSlowDelay; // Time before slow tooltips appears (FIXME: This is temporary until we merge in tooltip timer+priority work)
ImVector<char> ClipboardHandlerData; // If no custom clipboard handler is defined
ImVector<ImGuiID> MenusIdSubmittedThisFrame; // A list of menu IDs that were rendered at least once
@@ -1837,8 +1884,9 @@ struct ImGuiContext
// Debug Tools
bool DebugItemPickerActive; // Item picker is active (started with DebugStartItemPicker())
- ImGuiID DebugItemPickerBreakId; // Will call IM_DEBUG_BREAK() when encountering this id
+ ImGuiID DebugItemPickerBreakId; // Will call IM_DEBUG_BREAK() when encountering this ID
ImGuiMetricsConfig DebugMetricsConfig;
+ ImGuiStackTool DebugStackTool;
// Misc
float FramerateSecPerFrame[120]; // Calculate estimate of framerate for user over the last 2 seconds.
@@ -1864,7 +1912,6 @@ struct ImGuiContext
WithinFrameScope = WithinFrameScopeWithImplicitWindow = WithinEndChild = false;
GcCompactAll = false;
TestEngineHookItems = false;
- TestEngineHookIdInfo = 0;
TestEngine = NULL;
WindowsActiveCount = 0;
@@ -1876,6 +1923,7 @@ struct ImGuiContext
WheelingWindow = NULL;
WheelingWindowTimer = 0.0f;
+ DebugHookIdInfo = 0;
HoveredId = HoveredIdPreviousFrame = 0;
HoveredIdAllowOverlap = false;
HoveredIdUsingMouseWheel = HoveredIdPreviousFrameUsingMouseWheel = false;
@@ -1914,12 +1962,11 @@ struct ImGuiContext
ViewportFrontMostStampCount = 0;
NavWindow = NULL;
- NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavInputId = 0;
+ NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavActivateInputId = 0;
NavJustTabbedId = NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0;
+ NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None;
NavJustMovedToKeyMods = ImGuiKeyModFlags_None;
NavInputSource = ImGuiInputSource_None;
- NavScoringRect = ImRect();
- NavScoringCount = 0;
NavLayer = ImGuiNavLayer_Main;
NavIdTabCounter = INT_MAX;
NavIdIsAlive = false;
@@ -1930,21 +1977,23 @@ struct ImGuiContext
NavInitRequest = false;
NavInitRequestFromMove = false;
NavInitResultId = 0;
- NavMoveRequest = false;
- NavMoveRequestFlags = ImGuiNavMoveFlags_None;
- NavMoveRequestForward = ImGuiNavForward_None;
- NavMoveRequestKeyMods = ImGuiKeyModFlags_None;
- NavMoveDir = NavMoveDirLast = NavMoveClipDir = ImGuiDir_None;
- NavWrapRequestWindow = NULL;
- NavWrapRequestFlags = ImGuiNavMoveFlags_None;
+ NavMoveSubmitted = false;
+ NavMoveScoringItems = false;
+ NavMoveForwardToNextFrame = false;
+ NavMoveFlags = ImGuiNavMoveFlags_None;
+ NavMoveScrollFlags = ImGuiScrollFlags_None;
+ NavMoveKeyMods = ImGuiKeyModFlags_None;
+ NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None;
+ NavScoringDebugCount = 0;
+ NavTabbingInputableRemaining = 0;
NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL;
NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f;
NavWindowingToggleLayer = false;
TabFocusRequestCurrWindow = TabFocusRequestNextWindow = NULL;
- TabFocusRequestCurrCounterRegular = TabFocusRequestCurrCounterTabStop = INT_MAX;
- TabFocusRequestNextCounterRegular = TabFocusRequestNextCounterTabStop = INT_MAX;
+ TabFocusRequestCurrCounterTabStop = INT_MAX;
+ TabFocusRequestNextCounterTabStop = INT_MAX;
TabFocusPressed = false;
DimBgRatio = 0.0f;
@@ -1966,17 +2015,17 @@ struct ImGuiContext
CurrentTableStackIdx = -1;
CurrentTabBar = NULL;
- LastValidMousePos = ImVec2(0.0f, 0.0f);
TempInputId = 0;
ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_;
ColorEditLastHue = ColorEditLastSat = 0.0f;
- ColorEditLastColor[0] = ColorEditLastColor[1] = ColorEditLastColor[2] = FLT_MAX;
+ ColorEditLastColor = 0;
SliderCurrentAccum = 0.0f;
SliderCurrentAccumDirty = false;
DragCurrentAccumDirty = false;
DragCurrentAccum = 0.0f;
DragSpeedDefaultRatio = 1.0f / 100.0f;
DisabledAlphaBackup = 0.0f;
+ DisabledStackSize = 0;
ScrollbarClickDeltaToGrabCenter = 0.0f;
TooltipOverrideCount = 0;
TooltipSlowDelay = 0.50f;
@@ -2052,7 +2101,6 @@ struct IMGUI_API ImGuiWindowTempData
int CurrentTableIdx; // Current table index (into g.Tables)
ImGuiLayoutType LayoutType;
ImGuiLayoutType ParentLayoutType; // Layout type of parent window at the time of Begin()
- int FocusCounterRegular; // (Legacy Focus/Tabbing system) Sequential counter, start at -1 and increase as assigned via FocusableItemRegister() (FIXME-NAV: Needs redesign)
int FocusCounterTabStop; // (Legacy Focus/Tabbing system) Same, but only count widgets which you can Tab through.
// Local parameters stacks
@@ -2061,7 +2109,6 @@ struct IMGUI_API ImGuiWindowTempData
float TextWrapPos; // Current text wrap pos.
ImVector<float> ItemWidthStack; // Store item widths to restore (attention: .back() is not == ItemWidth)
ImVector<float> TextWrapPosStack; // Store text wrap pos to restore (attention: .back() is not == TextWrapPos)
- ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting
};
// Storage for one window
@@ -2153,8 +2200,9 @@ struct IMGUI_API ImGuiWindow
ImDrawList* DrawList; // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer)
ImDrawList DrawListInst;
- ImGuiWindow* ParentWindow; // If we are a child _or_ popup window, this is pointing to our parent. Otherwise NULL.
- ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. Doesn't cross through dock nodes. We use this so IsWindowFocused() can behave consistently regardless of docking state.
+ ImGuiWindow* ParentWindow; // If we are a child _or_ popup _or_ docked window, this is pointing to our parent. Otherwise NULL.
+ ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. Doesn't cross through popups/dock nodes.
+ ImGuiWindow* RootWindowPopupTree; // Point to ourself or first ancestor that is not a child window. Cross through popups parent<>child.
ImGuiWindow* RootWindowDockTree; // Point to ourself or first ancestor that is not a child window. Cross through dock nodes.
ImGuiWindow* RootWindowForTitleBarHighlight; // Point to ourself or first ancestor which will display TitleBgActive color when this window is active.
ImGuiWindow* RootWindowForNav; // Point to ourself or first ancestor which doesn't have the NavFlattened flag.
@@ -2559,7 +2607,7 @@ namespace ImGui
IMGUI_API ImGuiWindow* FindWindowByName(const char* name);
IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window);
IMGUI_API ImVec2 CalcWindowNextAutoFitSize(ImGuiWindow* window);
- IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent);
+ IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy, bool dock_hierarchy);
IMGUI_API bool IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below);
IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window);
IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0);
@@ -2617,7 +2665,14 @@ namespace ImGui
IMGUI_API void SetScrollY(ImGuiWindow* window, float scroll_y);
IMGUI_API void SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio);
IMGUI_API void SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio);
- IMGUI_API ImVec2 ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect);
+
+ // Early work-in-progress API (ScrollToItem() will become public)
+ IMGUI_API void ScrollToItem(ImGuiScrollFlags flags = 0);
+ IMGUI_API void ScrollToRect(ImGuiWindow* window, const ImRect& rect, ImGuiScrollFlags flags = 0);
+ IMGUI_API ImVec2 ScrollToRectEx(ImGuiWindow* window, const ImRect& rect, ImGuiScrollFlags flags = 0);
+//#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+ inline void ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& rect) { ScrollToRect(window, rect, ImGuiScrollFlags_KeepVisibleEdgeY); }
+//#endif
// Basic Accessors
inline ImGuiID GetItemID() { ImGuiContext& g = *GImGui; return g.LastItemData.ID; } // Get ID of last item (~~ often same ImGui::GetID(label) beforehand)
@@ -2638,10 +2693,10 @@ namespace ImGui
// Basic Helpers for widget code
IMGUI_API void ItemSize(const ImVec2& size, float text_baseline_y = -1.0f);
IMGUI_API void ItemSize(const ImRect& bb, float text_baseline_y = -1.0f);
- IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL, ImGuiItemAddFlags flags = 0);
+ IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL, ImGuiItemFlags extra_flags = 0);
IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id);
- IMGUI_API void ItemFocusable(ImGuiWindow* window, ImGuiID id);
- IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged);
+ IMGUI_API void ItemInputable(ImGuiWindow* window, ImGuiID id);
+ IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id);
IMGUI_API void SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect);
IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h);
IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x);
@@ -2655,12 +2710,14 @@ namespace ImGui
IMGUI_API void PopItemFlag();
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+ // Currently refactoring focus/nav/tabbing system
// If you have old/custom copy-and-pasted widgets that used FocusableItemRegister():
- // (Old) IMGUI_VERSION_NUM < 18209: using 'ItemAdd(....)' and 'bool focused = FocusableItemRegister(...)'
- // (New) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0'
+ // (Old) IMGUI_VERSION_NUM < 18209: using 'ItemAdd(....)' and 'bool tab_focused = FocusableItemRegister(...)'
+ // (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool tab_focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0'
+ // (New) IMGUI_VERSION_NUM >= 18413: using 'ItemAdd(..., ImGuiItemFlags_Inputable)' and 'bool tab_focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_FocusedTabbing) != 0 || g.NavActivateInputId == id' (WIP)
// Widget code are simplified as there's no need to call FocusableItemUnregister() while managing the transition from regular widget to TempInputText()
- inline bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id) { IM_ASSERT(0); IM_UNUSED(window); IM_UNUSED(id); return false; } // -> pass ImGuiItemAddFlags_Focusable flag to ItemAdd()
- inline IM_NORETURN void FocusableItemUnregister(ImGuiWindow* window) { IM_ASSERT(0); IM_UNUSED(window); } // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem
+ inline bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id) { IM_ASSERT(0); IM_UNUSED(window); IM_UNUSED(id); return false; } // -> pass ImGuiItemAddFlags_Inputable flag to ItemAdd()
+ inline void FocusableItemUnregister(ImGuiWindow* window) { IM_ASSERT(0); IM_UNUSED(window); } // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem
#endif
// Logging/Capture
@@ -2674,6 +2731,7 @@ namespace ImGui
IMGUI_API void OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags = ImGuiPopupFlags_None);
IMGUI_API void ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup);
IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup);
+ IMGUI_API void ClosePopupsExceptModals();
IMGUI_API bool IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags);
IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags);
IMGUI_API void BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags);
@@ -2681,9 +2739,10 @@ namespace ImGui
IMGUI_API ImGuiWindow* GetTopMostPopupModal();
IMGUI_API ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window);
IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy);
- IMGUI_API bool BeginViewportSideBar(const char* name, ImGuiViewport* viewport, ImGuiDir dir, float size, ImGuiWindowFlags window_flags);
// Menus
+ IMGUI_API bool BeginViewportSideBar(const char* name, ImGuiViewport* viewport, ImGuiDir dir, float size, ImGuiWindowFlags window_flags);
+ IMGUI_API bool BeginMenuEx(const char* label, const char* icon, bool enabled = true);
IMGUI_API bool MenuItemEx(const char* label, const char* icon, const char* shortcut = NULL, bool selected = false, bool enabled = true);
// Combos
@@ -2693,9 +2752,13 @@ namespace ImGui
// Gamepad/Keyboard Navigation
IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit);
+ IMGUI_API void NavInitRequestApplyResult();
IMGUI_API bool NavMoveRequestButNoResultYet();
+ IMGUI_API void NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags);
+ IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags);
+ IMGUI_API void NavMoveRequestResolveWithLastItem();
IMGUI_API void NavMoveRequestCancel();
- IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags);
+ IMGUI_API void NavMoveRequestApplyResult();
IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags);
IMGUI_API float GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode);
IMGUI_API ImVec2 GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor = 0.0f, float fast_factor = 0.0f);
@@ -2956,10 +3019,12 @@ namespace ImGui
// Debug Tools
IMGUI_API void ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL);
+ IMGUI_API void ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL);
inline void DebugDrawItemRect(ImU32 col = IM_COL32(255,0,0,255)) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; GetForegroundDrawList(window)->AddRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, col); }
inline void DebugStartItemPicker() { ImGuiContext& g = *GImGui; g.DebugItemPickerActive = true; }
IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas);
+ IMGUI_API void DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end);
IMGUI_API void DebugNodeColumns(ImGuiOldColumns* columns);
IMGUI_API void DebugNodeDockNode(ImGuiDockNode* node, const char* label);
IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label);
@@ -3006,14 +3071,12 @@ IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table
#ifdef IMGUI_ENABLE_TEST_ENGINE
extern void ImGuiTestEngineHook_ItemAdd(ImGuiContext* ctx, const ImRect& bb, ImGuiID id);
extern void ImGuiTestEngineHook_ItemInfo(ImGuiContext* ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags);
-extern void ImGuiTestEngineHook_IdInfo(ImGuiContext* ctx, ImGuiDataType data_type, ImGuiID id, const void* data_id);
-extern void ImGuiTestEngineHook_IdInfo(ImGuiContext* ctx, ImGuiDataType data_type, ImGuiID id, const void* data_id, const void* data_id_end);
extern void ImGuiTestEngineHook_Log(ImGuiContext* ctx, const char* fmt, ...);
+extern const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiID id);
+
#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemAdd(&g, _BB, _ID) // Register item bounding box
#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register item label and status flags (optional)
#define IMGUI_TEST_ENGINE_LOG(_FMT,...) if (g.TestEngineHookItems) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log
-#define IMGUI_TEST_ENGINE_ID_INFO(_ID,_TYPE,_DATA) if (g.TestEngineHookIdInfo == _ID) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA));
-#define IMGUI_TEST_ENGINE_ID_INFO2(_ID,_TYPE,_DATA,_DATA2) if (g.TestEngineHookIdInfo == _ID) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA), (const void*)(_DATA2));
#else
#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) ((void)0)
#endif
diff --git a/imgui/imgui_tables.cpp b/imgui/imgui_tables.cpp
index 71ac00f6..56056ae3 100644
--- a/imgui/imgui_tables.cpp
+++ b/imgui/imgui_tables.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.84
+// dear imgui, v1.85
// (tables and columns code)
/*
@@ -324,7 +324,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
const ImVec2 avail_size = GetContentRegionAvail();
ImVec2 actual_outer_size = CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f);
ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size);
- if (use_child_window && IsClippedEx(outer_rect, 0, false))
+ if (use_child_window && IsClippedEx(outer_rect, 0))
{
ItemSize(outer_rect);
return false;
@@ -564,6 +564,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
// + 0 (for ImGuiTable instance, we are pooling allocations in g.Tables)
// + 1 (for table->RawData allocated below)
// + 1 (for table->ColumnsNames, if names are used)
+// Shared allocations per number of nested tables
// + 1 (for table->Splitter._Channels)
// + 2 * active_channels_count (for ImDrawCmd and ImDrawIdx buffers inside channels)
// Where active_channels_count is variable but often == columns_count or columns_count + 1, see TableSetupDrawChannels() for details.
@@ -1170,7 +1171,7 @@ void ImGui::TableUpdateBorders(ImGuiTable* table)
KeepAliveID(column_id);
bool hovered = false, held = false;
- bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick);
+ bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_NoNavFocus);
if (pressed && IsMouseDoubleClicked(0))
{
TableSetColumnWidthAutoSingle(table, column_n);
@@ -1479,6 +1480,7 @@ void ImGui::TableSetupScrollFreeze(int columns, int rows)
table->IsUnfrozenRows = (table->FreezeRowsCount == 0); // Make sure this is set before TableUpdateLayout() so ImGuiListClipper can benefit from it.b
// Ensure frozen columns are ordered in their section. We still allow multiple frozen columns to be reordered.
+ // FIXME-TABLE: This work for preserving 2143 into 21|43. How about 4321 turning into 21|43? (preserve relative order in each section)
for (int column_n = 0; column_n < table->FreezeColumnsRequest; column_n++)
{
int order_n = table->DisplayOrderToIndex[column_n];
@@ -3987,7 +3989,7 @@ void ImGui::EndColumns()
const float column_hit_hw = COLUMNS_HIT_RECT_HALF_WIDTH;
const ImRect column_hit_rect(ImVec2(x - column_hit_hw, y1), ImVec2(x + column_hit_hw, y2));
KeepAliveID(column_id);
- if (IsClippedEx(column_hit_rect, column_id, false))
+ if (IsClippedEx(column_hit_rect, column_id)) // FIXME: Can be removed or replaced with a lower-level test
continue;
bool hovered = false, held = false;
diff --git a/imgui/imgui_widgets.cpp b/imgui/imgui_widgets.cpp
index f8d13bcd..e0215853 100644
--- a/imgui/imgui_widgets.cpp
+++ b/imgui/imgui_widgets.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.84
+// dear imgui, v1.85
// (widgets code)
/*
@@ -152,9 +152,13 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags)
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
-
ImGuiContext& g = *GImGui;
- IM_ASSERT(text != NULL);
+
+ // Accept null ranges
+ if (text == text_end)
+ text = text_end = "";
+
+ // Calculate length
const char* text_begin = text;
if (text_end == NULL)
text_end = text + strlen(text); // FIXME-OPT
@@ -201,7 +205,7 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags)
ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height));
while (line < text_end)
{
- if (IsClippedEx(line_rect, 0, false))
+ if (IsClippedEx(line_rect, 0))
break;
const char* line_end = (const char*)memchr(line, '\n', text_end - line);
@@ -269,6 +273,7 @@ void ImGui::TextV(const char* fmt, va_list args)
if (window->SkipItems)
return;
+ // FIXME-OPT: Handle the %s shortcut?
ImGuiContext& g = *GImGui;
const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
TextEx(g.TempBuffer, text_end, ImGuiTextFlags_NoWidthForLargeClippedText);
@@ -566,6 +571,8 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
ClearActiveID();
else
SetActiveID(id, window); // Hold on ID
+ if (!(flags & ImGuiButtonFlags_NoNavFocus))
+ SetFocusID(id, window);
g.ActiveIdMouseButton = mouse_button_clicked;
FocusWindow(window);
}
@@ -576,6 +583,8 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
const bool has_repeated_at_least_once = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay;
if (!has_repeated_at_least_once)
pressed = true;
+ if (!(flags & ImGuiButtonFlags_NoNavFocus))
+ SetFocusID(id, window);
ClearActiveID();
}
@@ -600,13 +609,12 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
bool nav_activated_by_code = (g.NavActivateId == id);
bool nav_activated_by_inputs = IsNavInputTest(ImGuiNavInput_Activate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiInputReadMode_Repeat : ImGuiInputReadMode_Pressed);
if (nav_activated_by_code || nav_activated_by_inputs)
- pressed = true;
- if (nav_activated_by_code || nav_activated_by_inputs || g.ActiveId == id)
{
// Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button.
- g.NavActivateId = id; // This is so SetActiveId assign a Nav source
+ pressed = true;
SetActiveID(id, window);
- if ((nav_activated_by_code || nav_activated_by_inputs) && !(flags & ImGuiButtonFlags_NoNavFocus))
+ g.ActiveIdSource = ImGuiInputSource_Nav;
+ if (!(flags & ImGuiButtonFlags_NoNavFocus))
SetFocusID(id, window);
}
}
@@ -1583,7 +1591,7 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF
bool pressed = ButtonBehavior(bb, id, &hovered, &held);
const ImGuiID popup_id = ImHashStr("##ComboPopup", 0, id);
bool popup_open = IsPopupOpen(popup_id, ImGuiPopupFlags_None);
- if ((pressed || g.NavActivateId == id) && !popup_open)
+ if (pressed && !popup_open)
{
OpenPopupEx(popup_id, ImGuiPopupFlags_None);
popup_open = true;
@@ -1797,7 +1805,7 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi
bool value_changed = false;
for (int i = 0; i < items_count; i++)
{
- PushID((void*)(intptr_t)i);
+ PushID(i);
const bool item_selected = (i == *current_item);
const char* item_text;
if (!items_getter(data, i, &item_text))
@@ -2391,7 +2399,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0;
ItemSize(total_bb, style.FramePadding.y);
- if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemAddFlags_Focusable : 0))
+ if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0))
return false;
// Default format string when passing NULL
@@ -2405,24 +2413,26 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);
if (!temp_input_is_active)
{
- const bool focus_requested = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Focused) != 0;
+ const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0;
const bool clicked = (hovered && g.IO.MouseClicked[0]);
const bool double_clicked = (hovered && g.IO.MouseDoubleClicked[0]);
- if (focus_requested || clicked || double_clicked || g.NavActivateId == id || g.NavInputId == id)
+ if (input_requested_by_tabbing || clicked || double_clicked || g.NavActivateId == id || g.NavActivateInputId == id)
{
SetActiveID(id, window);
SetFocusID(id, window);
FocusWindow(window);
g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);
- if (temp_input_allowed && (focus_requested || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavInputId == id))
- temp_input_is_active = true;
+ if (temp_input_allowed)
+ if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavActivateInputId == id)
+ temp_input_is_active = true;
}
+
// Experimental: simple click (without moving) turns Drag into an InputText
- // FIXME: Currently polling ImGuiConfigFlags_IsTouchScreen, may either poll an hypothetical ImGuiBackendFlags_HasKeyboard and/or an explicit drag settings.
if (g.IO.ConfigDragClickToInputText && temp_input_allowed && !temp_input_is_active)
if (g.ActiveId == id && hovered && g.IO.MouseReleased[0] && !IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR))
{
- g.NavInputId = id;
+ g.NavActivateId = g.NavActivateInputId = id;
+ g.NavActivateFlags = ImGuiActivateFlags_PreferInput;
temp_input_is_active = true;
}
}
@@ -3007,7 +3017,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0;
ItemSize(total_bb, style.FramePadding.y);
- if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemAddFlags_Focusable : 0))
+ if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0))
return false;
// Default format string when passing NULL
@@ -3021,15 +3031,15 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);
if (!temp_input_is_active)
{
- const bool focus_requested = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Focused) != 0;
+ const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0;
const bool clicked = (hovered && g.IO.MouseClicked[0]);
- if (focus_requested || clicked || g.NavActivateId == id || g.NavInputId == id)
+ if (input_requested_by_tabbing || clicked || g.NavActivateId == id || g.NavActivateInputId == id)
{
SetActiveID(id, window);
SetFocusID(id, window);
FocusWindow(window);
g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);
- if (temp_input_allowed && (focus_requested || (clicked && g.IO.KeyCtrl) || g.NavInputId == id))
+ if (temp_input_allowed && (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || g.NavActivateInputId == id))
temp_input_is_active = true;
}
}
@@ -3181,7 +3191,7 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d
format = PatchFormatStringFloatToInt(format);
const bool hovered = ItemHoverable(frame_bb, id);
- if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id)
+ if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavActivateInputId == id)
{
SetActiveID(id, window);
SetFocusID(id, window);
@@ -3445,7 +3455,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data
style.FramePadding.x = style.FramePadding.y;
ImGuiButtonFlags button_flags = ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups;
if (flags & ImGuiInputTextFlags_ReadOnly)
- BeginDisabled(true);
+ BeginDisabled();
SameLine(0, style.ItemInnerSpacing.x);
if (ButtonEx("-", ImVec2(button_size, button_size), button_flags))
{
@@ -3975,15 +3985,18 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
ImGuiItemStatusFlags item_status_flags = 0;
if (is_multiline)
{
- if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemAddFlags_Focusable))
+ ImVec2 backup_pos = window->DC.CursorPos;
+ ItemSize(total_bb, style.FramePadding.y);
+ if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable))
{
- ItemSize(total_bb, style.FramePadding.y);
EndGroup();
return false;
}
item_status_flags = g.LastItemData.StatusFlags;
+ window->DC.CursorPos = backup_pos;
// We reproduce the contents of BeginChildFrame() in order to provide 'label' so our window internal data are easier to read/debug.
+ // FIXME-NAV: Pressing NavActivate will trigger general child activation right before triggering our own below. Harmless but bizarre.
PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
@@ -4006,7 +4019,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// Support for internal ImGuiInputTextFlags_MergedItem flag, which could be redesigned as an ItemFlags if needed (with test performed in ItemAdd)
ItemSize(total_bb, style.FramePadding.y);
if (!(flags & ImGuiInputTextFlags_MergedItem))
- if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemAddFlags_Focusable))
+ if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable))
return false;
item_status_flags = g.LastItemData.StatusFlags;
}
@@ -4017,21 +4030,19 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// We are only allowed to access the state if we are already the active widget.
ImGuiInputTextState* state = GetInputTextState(id);
- const bool focus_requested_by_code = (item_status_flags & ImGuiItemStatusFlags_FocusedByCode) != 0;
- const bool focus_requested_by_tabbing = (item_status_flags & ImGuiItemStatusFlags_FocusedByTabbing) != 0;
+ const bool input_requested_by_tabbing = (item_status_flags & ImGuiItemStatusFlags_FocusedByTabbing) != 0;
+ const bool input_requested_by_nav = (g.ActiveId != id) && ((g.NavActivateInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_Keyboard));
const bool user_clicked = hovered && io.MouseClicked[0];
- const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_Keyboard));
const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == GetWindowScrollbarID(draw_window, ImGuiAxis_Y);
const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == GetWindowScrollbarID(draw_window, ImGuiAxis_Y);
-
bool clear_active_id = false;
- bool select_all = (g.ActiveId != id) && ((flags & ImGuiInputTextFlags_AutoSelectAll) != 0 || user_nav_input_start) && (!is_multiline);
+ bool select_all = false;
float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX;
const bool init_changed_specs = (state != NULL && state->Stb.single_line != !is_multiline);
- const bool init_make_active = (user_clicked || user_scroll_finish || user_nav_input_start || focus_requested_by_code || focus_requested_by_tabbing);
+ const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav || input_requested_by_tabbing);
const bool init_state = (init_make_active || user_scroll_active);
if ((init_state && g.ActiveId != id) || init_changed_specs)
{
@@ -4067,13 +4078,20 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
state->ID = id;
state->ScrollX = 0.0f;
stb_textedit_initialize_state(&state->Stb, !is_multiline);
- if (!is_multiline && focus_requested_by_code)
+ }
+
+ if (!is_multiline)
+ {
+ if (flags & ImGuiInputTextFlags_AutoSelectAll)
+ select_all = true;
+ if (input_requested_by_nav && (!recycle_state || !(g.NavActivateFlags & ImGuiActivateFlags_TryToPreserveState)))
+ select_all = true;
+ if (input_requested_by_tabbing || (user_clicked && io.KeyCtrl))
select_all = true;
}
+
if (flags & ImGuiInputTextFlags_AlwaysOverwrite)
state->Stb.insert_mode = 1; // stb field name is indeed incorrect (see #2863)
- if (!is_multiline && (focus_requested_by_tabbing || (user_clicked && io.KeyCtrl)))
- select_all = true;
}
if (g.ActiveId != id && init_make_active)
@@ -4106,7 +4124,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// Lock the decision of whether we are going to take the path displaying the cursor or selection
const bool render_cursor = (g.ActiveId == id) || (state && user_scroll_active);
- bool render_selection = state && state->HasSelection() && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor);
+ bool render_selection = state && (state->HasSelection() || select_all) && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor);
bool value_changed = false;
bool enter_pressed = false;
@@ -4207,7 +4225,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters.
if (io.InputQueueCharacters.Size > 0)
{
- if (!ignore_char_inputs && !is_readonly && !user_nav_input_start)
+ if (!ignore_char_inputs && !is_readonly && !input_requested_by_nav)
for (int n = 0; n < io.InputQueueCharacters.Size; n++)
{
// Insert character if they pass filtering
@@ -4248,6 +4266,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
const bool is_undo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Z)) && !is_readonly && is_undoable);
const bool is_redo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressedMap(ImGuiKey_Z))) && !is_readonly && is_undoable;
+ // We allow validate/cancel with Nav source (gamepad) to makes it easier to undo an accidental NavInput press with no keyboard wired, but otherwise it isn't very useful.
+ const bool is_validate_enter = IsKeyPressedMap(ImGuiKey_Enter) || IsKeyPressedMap(ImGuiKey_KeyPadEnter);
+ const bool is_validate_nav = (IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed) && !IsKeyPressedMap(ImGuiKey_Space)) || IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed);
+ const bool is_cancel = IsKeyPressedMap(ImGuiKey_Escape) || IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed);
+
if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); }
@@ -4268,7 +4291,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
}
state->OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask);
}
- else if (IsKeyPressedMap(ImGuiKey_Enter) || IsKeyPressedMap(ImGuiKey_KeyPadEnter))
+ else if (is_validate_enter)
{
bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0;
if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl))
@@ -4282,7 +4305,12 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
state->OnKeyPressed((int)c);
}
}
- else if (IsKeyPressedMap(ImGuiKey_Escape))
+ else if (is_validate_nav)
+ {
+ IM_ASSERT(!is_validate_enter);
+ enter_pressed = clear_active_id = true;
+ }
+ else if (is_cancel)
{
clear_active_id = cancel_edit = true;
}
@@ -4767,6 +4795,30 @@ bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flag
return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha);
}
+// ColorEdit supports RGB and HSV inputs. In case of RGB input resulting color may have undefined hue and/or saturation.
+// Since widget displays both RGB and HSV values we must preserve hue and saturation to prevent these values resetting.
+static void ColorEditRestoreHS(const float* col, float* H, float* S, float* V)
+{
+ // This check is optional. Suppose we have two color widgets side by side, both widgets display different colors, but both colors have hue and/or saturation undefined.
+ // With color check: hue/saturation is preserved in one widget. Editing color in one widget would reset hue/saturation in another one.
+ // Without color check: common hue/saturation would be displayed in all widgets that have hue/saturation undefined.
+ // g.ColorEditLastColor is stored as ImU32 RGB value: this essentially gives us color equality check with reduced precision.
+ // Tiny external color changes would not be detected and this check would still pass. This is OK, since we only restore hue/saturation _only_ if they are undefined,
+ // therefore this change flipping hue/saturation from undefined to a very tiny value would still be represented in color picker.
+ ImGuiContext& g = *GImGui;
+ if (g.ColorEditLastColor != ImGui::ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0)))
+ return;
+
+ // When S == 0, H is undefined.
+ // When H == 1 it wraps around to 0.
+ if (*S == 0.0f || (*H == 0.0f && g.ColorEditLastHue == 1))
+ *H = g.ColorEditLastHue;
+
+ // When V == 0, S is undefined.
+ if (*V == 0.0f)
+ *S = g.ColorEditLastSat;
+}
+
// Edit colors components (each component in 0.0f..1.0f range).
// See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
// With typical options: Left-click on color square to open color picker. Right-click to open option menu. CTRL-Click over input fields to edit them and TAB to go to next item.
@@ -4822,13 +4874,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
{
// Hue is lost when converting from greyscale rgb (saturation=0). Restore it.
ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]);
- if (memcmp(g.ColorEditLastColor, col, sizeof(float) * 3) == 0)
- {
- if (f[1] == 0)
- f[0] = g.ColorEditLastHue;
- if (f[2] == 0)
- f[1] = g.ColorEditLastSat;
- }
+ ColorEditRestoreHS(col, &f[0], &f[1], &f[2]);
}
int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) };
@@ -4963,7 +5009,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
g.ColorEditLastHue = f[0];
g.ColorEditLastSat = f[1];
ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]);
- memcpy(g.ColorEditLastColor, f, sizeof(float) * 3);
+ g.ColorEditLastColor = ColorConvertFloat4ToU32(ImVec4(f[0], f[1], f[2], 0));
}
if ((flags & ImGuiColorEditFlags_DisplayRGB) && (flags & ImGuiColorEditFlags_InputHSV))
ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]);
@@ -5098,13 +5144,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
{
// Hue is lost when converting from greyscale rgb (saturation=0). Restore it.
ColorConvertRGBtoHSV(R, G, B, H, S, V);
- if (memcmp(g.ColorEditLastColor, col, sizeof(float) * 3) == 0)
- {
- if (S == 0)
- H = g.ColorEditLastHue;
- if (V == 0)
- S = g.ColorEditLastSat;
- }
+ ColorEditRestoreHS(col, &H, &S, &V);
}
else if (flags & ImGuiColorEditFlags_InputHSV)
{
@@ -5157,6 +5197,10 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
{
S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size - 1));
V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1));
+
+ // Greatly reduces hue jitter and reset to 0 when hue == 255 and color is rapidly modified using SV square.
+ if (g.ColorEditLastColor == ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0)))
+ H = g.ColorEditLastHue;
value_changed = value_changed_sv = true;
}
if (!(flags & ImGuiColorEditFlags_NoOptions))
@@ -5230,10 +5274,10 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
{
if (flags & ImGuiColorEditFlags_InputRGB)
{
- ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10 * 1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]);
+ ColorConvertHSVtoRGB(H, S, V, col[0], col[1], col[2]);
g.ColorEditLastHue = H;
g.ColorEditLastSat = S;
- memcpy(g.ColorEditLastColor, col, sizeof(float) * 3);
+ g.ColorEditLastColor = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0));
}
else if (flags & ImGuiColorEditFlags_InputHSV)
{
@@ -5287,13 +5331,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
G = col[1];
B = col[2];
ColorConvertRGBtoHSV(R, G, B, H, S, V);
- if (memcmp(g.ColorEditLastColor, col, sizeof(float) * 3) == 0) // Fix local Hue as display below will use it immediately.
- {
- if (S == 0)
- H = g.ColorEditLastHue;
- if (V == 0)
- S = g.ColorEditLastSat;
- }
+ ColorEditRestoreHS(col, &H, &S, &V); // Fix local Hue as display below will use it immediately.
}
else if (flags & ImGuiColorEditFlags_InputHSV)
{
@@ -5900,12 +5938,12 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
toggled = true;
}
- if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Left && is_open)
+ if (g.NavId == id && g.NavMoveDir == ImGuiDir_Left && is_open)
{
toggled = true;
NavMoveRequestCancel();
}
- if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority?
+ 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;
NavMoveRequestCancel();
@@ -5976,7 +6014,7 @@ void ImGui::TreePush(const char* str_id)
ImGuiWindow* window = GetCurrentWindow();
Indent();
window->DC.TreeDepth++;
- PushID(str_id ? str_id : "#TreePush");
+ PushID(str_id);
}
void ImGui::TreePush(const void* ptr_id)
@@ -5984,7 +6022,7 @@ void ImGui::TreePush(const void* ptr_id)
ImGuiWindow* window = GetCurrentWindow();
Indent();
window->DC.TreeDepth++;
- PushID(ptr_id ? ptr_id : (const void*)"#TreePush");
+ PushID(ptr_id);
}
void ImGui::TreePushOverrideID(ImGuiID id)
@@ -5993,7 +6031,7 @@ void ImGui::TreePushOverrideID(ImGuiID id)
ImGuiWindow* window = g.CurrentWindow;
Indent();
window->DC.TreeDepth++;
- window->IDStack.push_back(id);
+ PushOverrideID(id);
}
void ImGui::TreePop()
@@ -6147,20 +6185,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
window->ClipRect.Max.x = window->ParentWorkRect.Max.x;
}
- bool item_add;
const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0;
- if (disabled_item)
- {
- ImGuiItemFlags backup_item_flags = g.CurrentItemFlags;
- g.CurrentItemFlags |= ImGuiItemFlags_Disabled;
- item_add = ItemAdd(bb, id);
- g.CurrentItemFlags = backup_item_flags;
- }
- else
- {
- item_add = ItemAdd(bb, id);
- }
-
+ const bool item_add = ItemAdd(bb, id, NULL, disabled_item ? ImGuiItemFlags_Disabled : ImGuiItemFlags_None);
if (span_all_columns)
{
window->ClipRect.Min.x = backup_clip_rect_min_x;
@@ -6172,7 +6198,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
const bool disabled_global = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
if (disabled_item && !disabled_global) // Only testing this as an optimization
- BeginDisabled(true);
+ BeginDisabled();
// FIXME: We can standardize the behavior of those two, we could also keep the fast path of override ClipRect + full push on render only,
// which would be advantageous since most selectable are not selected.
@@ -6209,7 +6235,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
{
if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent)
{
- SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent, ImRect(bb.Min - window->Pos, bb.Max - window->Pos));
+ SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent, ImRect(bb.Min - window->Pos, bb.Max - window->Pos)); // (bb == NavRect)
g.NavDisableHighlight = true;
}
}
@@ -6474,7 +6500,7 @@ int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_get
float v0 = values_getter(data, (0 + values_offset) % values_count);
float t0 = 0.0f;
ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) * inv_scale) ); // Point in the normalized space of our target rectangle
- float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (-scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f); // Where does the zero line stands
+ float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (1 + scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f); // Where does the zero line stands
const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram);
const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered);
@@ -6690,21 +6716,21 @@ void ImGui::EndMenuBar()
// Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings.
if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
{
+ // Try to find out if the request is for one of our child menu
ImGuiWindow* nav_earliest_child = g.NavWindow;
while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu))
nav_earliest_child = nav_earliest_child->ParentWindow;
- if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && g.NavMoveRequestForward == ImGuiNavForward_None)
+ if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0)
{
// To do so we claim focus back, restore NavId and then process the movement request for yet another frame.
- // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth the hassle/cost)
+ // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth bothering)
const ImGuiNavLayer layer = ImGuiNavLayer_Menu;
IM_ASSERT(window->DC.NavLayersActiveMaskNext & (1 << layer)); // Sanity check
FocusWindow(window);
SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]);
g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection.
g.NavDisableMouseHover = g.NavMousePosDirty = true;
- g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
- NavMoveRequestCancel();
+ NavMoveRequestForward(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags); // Repeat
}
}
@@ -6797,7 +6823,7 @@ void ImGui::EndMainMenuBar()
End();
}
-bool ImGui::BeginMenu(const char* label, bool enabled)
+bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
@@ -6860,17 +6886,19 @@ bool ImGui::BeginMenu(const char* label, bool enabled)
}
else
{
- // Menu inside a menu
+ // Menu inside a regular/vertical menu
// (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f.
// Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.
popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
- float icon_w = 0.0f; // FIXME: This not currently exposed for BeginMenu() however you can call window->DC.MenuColumns.DeclColumns(w, 0, 0, 0) yourself
+ float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f;
float checkmark_w = IM_FLOOR(g.FontSize * 1.20f);
float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, 0.0f, checkmark_w); // Feedback to next frame
float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w);
ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
pressed = Selectable("", menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f));
RenderText(text_pos, label);
+ if (icon_w > 0.0f)
+ RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon);
RenderArrow(window->DrawList, pos + ImVec2(offsets->OffsetMark + extra_w + g.FontSize * 0.30f, 0.0f), GetColorU32(ImGuiCol_Text), ImGuiDir_Right);
}
if (!enabled)
@@ -6887,38 +6915,30 @@ bool ImGui::BeginMenu(const char* label, bool enabled)
// Close menu when not hovering it anymore unless we are moving roughly in the direction of the menu
// Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.
bool moving_toward_other_child_menu = false;
-
ImGuiWindow* child_menu_window = (g.BeginPopupStack.Size < g.OpenPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].SourceWindow == window) ? g.OpenPopupStack[g.BeginPopupStack.Size].Window : NULL;
if (g.HoveredWindow == window && child_menu_window != NULL && !(window->Flags & ImGuiWindowFlags_MenuBar))
{
- // FIXME-DPI: Values should be derived from a master "scale" factor.
+ float ref_unit = g.FontSize; // FIXME-DPI
ImRect next_window_rect = child_menu_window->Rect();
- ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta;
+ ImVec2 ta = (g.IO.MousePos - g.IO.MouseDelta);
ImVec2 tb = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR();
ImVec2 tc = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR();
- float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack.
- ta.x += (window->Pos.x < child_menu_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues
- tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale?
- tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f);
+ float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, ref_unit * 0.5f, ref_unit * 2.5f); // add a bit of extra slack.
+ ta.x += (window->Pos.x < child_menu_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues (FIXME: ??)
+ tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -ref_unit * 8.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale?
+ tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +ref_unit * 8.0f);
moving_toward_other_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos);
- //GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG]
+ //GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_toward_other_child_menu ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG]
}
-
- // FIXME: Hovering a disabled BeginMenu or MenuItem won't close us
if (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_toward_other_child_menu)
want_close = true;
- if (!menu_is_open && hovered && pressed) // Click to open
+ // Open
+ if (!menu_is_open && pressed) // Click/activate to open
want_open = true;
else if (!menu_is_open && hovered && !moving_toward_other_child_menu) // Hover to open
want_open = true;
-
- if (g.NavActivateId == id)
- {
- want_close = menu_is_open;
- want_open = !menu_is_open;
- }
- if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open
+ if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open
{
want_open = true;
NavMoveRequestCancel();
@@ -6936,7 +6956,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled)
{
want_open = true;
}
- else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open
+ else if (g.NavId == id && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open
{
want_open = true;
NavMoveRequestCancel();
@@ -6975,6 +6995,11 @@ bool ImGui::BeginMenu(const char* label, bool enabled)
return menu_is_open;
}
+bool ImGui::BeginMenu(const char* label, bool enabled)
+{
+ return BeginMenuEx(label, NULL, enabled);
+}
+
void ImGui::EndMenu()
{
// Nav: When a left move request _within our child menu_ failed, close ourselves (the _parent_ menu).
@@ -6982,11 +7007,12 @@ void ImGui::EndMenu()
// However, it means that with the current code, a BeginMenu() from outside another menu or a menu-bar won't be closable with the Left direction.
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
- if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical)
- {
- ClosePopupToLevel(g.BeginPopupStack.Size, true);
- NavMoveRequestCancel();
- }
+ if (g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical)
+ if (g.NavWindow && (g.NavWindow->RootWindowForNav->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->RootWindowForNav->ParentWindow == window)
+ {
+ ClosePopupToLevel(g.BeginPopupStack.Size, true);
+ NavMoveRequestCancel();
+ }
EndPopup();
}
@@ -7007,7 +7033,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
bool pressed;
PushID(label);
if (!enabled)
- BeginDisabled(true);
+ BeginDisabled();
const ImGuiSelectableFlags flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover;
const ImGuiMenuColumns* offsets = &window->DC.MenuColumns;
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
@@ -7016,10 +7042,11 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
// Note that in this situation: we don't render the shortcut, we render a highlight instead of the selected tick mark.
float w = label_size.x;
window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f);
+ ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y));
pressed = Selectable("", selected, flags, ImVec2(w, 0.0f));
PopStyleVar();
- RenderText(pos + ImVec2(offsets->OffsetLabel, 0.0f), label);
+ RenderText(text_pos, label);
window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
}
else
@@ -7914,9 +7941,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
if (p_open && !*p_open)
{
- PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true);
- ItemAdd(ImRect(), id);
- PopItemFlag();
+ ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus);
return false;
}
@@ -7994,9 +8019,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
// and then gets submitted again, the tabs will have 'tab_appearing=true' but 'tab_is_new=false'.
if (tab_appearing && (!tab_bar_appearing || tab_is_new))
{
- PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true);
- ItemAdd(ImRect(), id);
- PopItemFlag();
+ ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus);
if (is_tab_button)
return false;
return tab_contents_visible;
@@ -8153,6 +8176,11 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
TabBarCloseTab(tab_bar, tab);
}
+ // Forward Hovered state so IsItemHovered() after Begin() can work (even though we are technically hovering our parent)
+ // That state is copied to window->DockTabItemStatusFlags by our caller.
+ if (docked_window && (hovered || g.HoveredId == close_button_id))
+ g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
+
// Restore main window position so user can draw there
if (want_clip_rect)
PopClipRect();
diff --git a/imgui/misc/freetype/imgui_freetype.cpp b/imgui/misc/freetype/imgui_freetype.cpp
index 06eb1758..a8ec51b3 100644
--- a/imgui/misc/freetype/imgui_freetype.cpp
+++ b/imgui/misc/freetype/imgui_freetype.cpp
@@ -6,6 +6,7 @@
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
+// 2021/08/23: fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL.
// 2021/03/05: added ImGuiFreeTypeBuilderFlags_Bitmap to load bitmap glyphs.
// 2021/03/02: set 'atlas->TexPixelsUseColors = true' to help some backends with deciding of a prefered texture format.
// 2021/01/28: added support for color-layered glyphs via ImGuiFreeTypeBuilderFlags_LoadColor (require Freetype 2.10+).