Skip to content

Commit 1dc3af3

Browse files
committed
Nav, Docking: reworked modal/ctrl+tab dimming system to be entirely processed at end of the frame, which will simplify things for an upcoming commit.
(Will backport some of this back to master now.)
1 parent c122c0e commit 1dc3af3

File tree

2 files changed

+91
-63
lines changed

2 files changed

+91
-63
lines changed

imgui.cpp

+90-63
Original file line numberDiff line numberDiff line change
@@ -959,7 +959,8 @@ static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVe
959959
static void RenderWindowOuterBorders(ImGuiWindow* window);
960960
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);
961961
static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
962-
static void EndFrameDrawDimmedBackgrounds();
962+
static void RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col);
963+
static void RenderDimmedBackgrounds();
963964

964965
// Viewports
965966
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.
@@ -4623,58 +4624,86 @@ static ImGuiWindow* FindFrontMostVisibleChildWindow(ImGuiWindow* window)
46234624
return window;
46244625
}
46254626

4626-
static void ImGui::EndFrameDrawDimmedBackgrounds()
4627+
static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col)
46274628
{
4628-
ImGuiContext& g = *GImGui;
4629+
if ((col & IM_COL32_A_MASK) == 0)
4630+
return;
46294631

4630-
// Draw modal whitening background on _other_ viewports than the one the modal is one
4631-
ImGuiWindow* modal_window = GetTopMostPopupModal();
4632-
const bool dim_bg_for_modal = (modal_window != NULL);
4633-
const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL);
4634-
if (dim_bg_for_modal || dim_bg_for_window_list)
4635-
for (int viewport_n = 0; viewport_n < g.Viewports.Size; viewport_n++)
4636-
{
4637-
ImGuiViewportP* viewport = g.Viewports[viewport_n];
4638-
if (modal_window && viewport == modal_window->Viewport)
4639-
continue;
4640-
if (g.NavWindowingListWindow && viewport == g.NavWindowingListWindow->Viewport)
4641-
continue;
4642-
if (g.NavWindowingTargetAnim && viewport == g.NavWindowingTargetAnim->Viewport)
4643-
continue;
4644-
if (viewport->Window && modal_window && IsWindowAbove(viewport->Window, modal_window))
4645-
continue;
4646-
ImDrawList* draw_list = GetForegroundDrawList(viewport);
4647-
const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
4648-
draw_list->AddRectFilled(viewport->Pos, viewport->Pos + viewport->Size, dim_bg_col);
4649-
}
4632+
ImGuiViewportP* viewport = window->Viewport;
4633+
ImRect viewport_rect = viewport->GetMainRect();
46504634

4651-
// Draw modal whitening background behind CTRL-TAB list
4652-
if (dim_bg_for_window_list && g.NavWindowingTargetAnim->Active)
4635+
// Draw behind window by moving the draw command at the FRONT of the draw list
4636+
{
4637+
ImDrawList* draw_list = window->RootWindowDockTree->DrawList;
4638+
draw_list->AddDrawCmd();
4639+
draw_list->PushClipRect(viewport_rect.Min - ImVec2(1, 1), viewport_rect.Max + ImVec2(1, 1), false); // Ensure ImDrawCmd are not merged
4640+
draw_list->AddRectFilled(viewport_rect.Min, viewport_rect.Max, col);
4641+
ImDrawCmd cmd = draw_list->CmdBuffer.back();
4642+
IM_ASSERT(cmd.ElemCount == 6);
4643+
draw_list->CmdBuffer.pop_back();
4644+
draw_list->CmdBuffer.push_front(cmd);
4645+
}
4646+
4647+
// Draw over sibling docking nodes in a same docking tree
4648+
if (window->RootWindow->DockIsActive)
46534649
{
4654-
// Choose a draw list that will be front-most across all our children
4655-
// In the unlikely case that the window wasn't made active we can't rely on its drawlist and skip rendering all-together.
4656-
ImGuiWindow* window = g.NavWindowingTargetAnim;
46574650
ImDrawList* draw_list = FindFrontMostVisibleChildWindow(window->RootWindowDockTree)->DrawList;
4658-
draw_list->PushClipRectFullScreen();
4651+
draw_list->PushClipRect(viewport_rect.Min, viewport_rect.Max, false);
4652+
//if (window->RootWindowDockTree != window->RootWindow)
4653+
RenderRectFilledWithHole(draw_list, window->RootWindowDockTree->Rect(), window->RootWindow->Rect(), col, 0.0f);// window->RootWindowDockTree->WindowRounding);
4654+
draw_list->PopClipRect();
4655+
}
4656+
}
46594657

4660-
// Docking: draw modal whitening background on other nodes of a same dock tree
4661-
// For CTRL+TAB within a docking node we need to render the dimming background in 8 steps
4662-
// (Because the root node renders the background in one shot, in order to avoid flickering when a child dock node is not submitted)
4663-
if (window->RootWindow->DockIsActive)
4664-
if (window->RootWindowDockTree != window->RootWindow)
4665-
RenderRectFilledWithHole(draw_list, window->RootWindowDockTree->Rect(), window->RootWindow->Rect(), GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio), g.Style.WindowRounding);
4658+
static void ImGui::RenderDimmedBackgrounds()
4659+
{
4660+
ImGuiContext& g = *GImGui;
4661+
ImGuiWindow* modal_window = GetTopMostAndVisiblePopupModal();
4662+
const bool dim_bg_for_modal = (modal_window != NULL);
4663+
const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL && g.NavWindowingTargetAnim->Active);
4664+
if (!dim_bg_for_modal && !dim_bg_for_window_list)
4665+
return;
4666+
4667+
ImGuiViewport* viewports_already_dimmed[2] = { NULL, NULL };
4668+
if (dim_bg_for_modal)
4669+
{
4670+
// Draw dimming behind modal
4671+
RenderDimmedBackgroundBehindWindow(modal_window, GetColorU32(ImGuiCol_ModalWindowDimBg, g.DimBgRatio));
4672+
viewports_already_dimmed[0] = modal_window->Viewport;
4673+
}
4674+
else if (dim_bg_for_window_list)
4675+
{
4676+
// Draw dimming behind CTRL+Tab target window and behind CTRL+Tab UI window
4677+
RenderDimmedBackgroundBehindWindow(g.NavWindowingTargetAnim, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio));
4678+
if (g.NavWindowingListWindow != NULL && g.NavWindowingListWindow->Viewport != g.NavWindowingTargetAnim->Viewport)
4679+
RenderDimmedBackgroundBehindWindow(g.NavWindowingListWindow, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio));
4680+
viewports_already_dimmed[0] = g.NavWindowingTargetAnim->Viewport;
4681+
viewports_already_dimmed[1] = g.NavWindowingListWindow ? g.NavWindowingListWindow->Viewport : NULL;
46664682

4667-
// Draw navigation selection/windowing rectangle border
4668-
float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
4683+
// Draw border around CTRL+Tab target window
4684+
ImGuiWindow* window = g.NavWindowingTargetAnim;
4685+
ImGuiViewport* viewport = window->Viewport;
4686+
float distance = g.FontSize;
46694687
ImRect bb = window->Rect();
4670-
bb.Expand(g.FontSize);
4671-
if (!window->Viewport->GetMainRect().Contains(bb)) // If a window fits the entire viewport, adjust its highlight inward
4672-
{
4673-
bb.Expand(-g.FontSize - 1.0f);
4674-
rounding = window->WindowRounding;
4675-
}
4676-
draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, 0, 3.0f);
4677-
draw_list->PopClipRect();
4688+
bb.Expand(distance);
4689+
if (bb.GetWidth() >= viewport->Size.x && bb.GetHeight() >= viewport->Size.y)
4690+
bb.Expand(-distance - 1.0f); // If a window fits the entire viewport, adjust its highlight inward
4691+
window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size);
4692+
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f);
4693+
window->DrawList->PopClipRect();
4694+
}
4695+
4696+
// Draw dimming background on _other_ viewports than the ones our windows are in
4697+
for (int viewport_n = 0; viewport_n < g.Viewports.Size; viewport_n++)
4698+
{
4699+
ImGuiViewportP* viewport = g.Viewports[viewport_n];
4700+
if (viewport == viewports_already_dimmed[0] || viewport == viewports_already_dimmed[1])
4701+
continue;
4702+
if (modal_window && viewport->Window && IsWindowAbove(viewport->Window, modal_window))
4703+
continue;
4704+
ImDrawList* draw_list = GetForegroundDrawList(viewport);
4705+
const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
4706+
draw_list->AddRectFilled(viewport->Pos, viewport->Pos + viewport->Size, dim_bg_col);
46784707
}
46794708
}
46804709

@@ -4740,9 +4769,6 @@ void ImGui::EndFrame()
47404769
// Initiate moving window + handle left-click and right-click focus
47414770
UpdateMouseMovingWindowEndFrame();
47424771

4743-
// Draw modal/window whitening backgrounds
4744-
EndFrameDrawDimmedBackgrounds();
4745-
47464772
// Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some)
47474773
UpdateViewportsEndFrame();
47484774

@@ -4785,6 +4811,7 @@ void ImGui::Render()
47854811

47864812
if (g.FrameCountEnded != g.FrameCount)
47874813
EndFrame();
4814+
const bool first_render_of_frame = (g.FrameCountRendered != g.FrameCount);
47884815
g.FrameCountRendered = g.FrameCount;
47894816
g.IO.MetricsRenderWindows = 0;
47904817

@@ -4814,6 +4841,10 @@ void ImGui::Render()
48144841
if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window
48154842
AddRootWindowToDrawData(windows_to_render_top_most[n]);
48164843

4844+
// Draw modal/window whitening backgrounds
4845+
if (first_render_of_frame)
4846+
RenderDimmedBackgrounds();
4847+
48174848
ImVec2 mouse_cursor_offset, mouse_cursor_size, mouse_cursor_uv[4];
48184849
if (g.IO.MouseDrawCursor && g.MouseCursor != ImGuiMouseCursor_None)
48194850
g.IO.Fonts->GetMouseCursorTexData(g.MouseCursor, &mouse_cursor_offset, &mouse_cursor_size, &mouse_cursor_uv[0], &mouse_cursor_uv[2]);
@@ -4827,7 +4858,7 @@ void ImGui::Render()
48274858

48284859
// Draw software mouse cursor if requested by io.MouseDrawCursor flag
48294860
// (note we scale cursor by current viewport/monitor, however Windows 10 for its own hardware cursor seems to be using a different scale factor)
4830-
if (mouse_cursor_size.x > 0.0f && mouse_cursor_size.y > 0.0f)
4861+
if (mouse_cursor_size.x > 0.0f && mouse_cursor_size.y > 0.0f && first_render_of_frame)
48314862
{
48324863
float scale = g.Style.MouseCursorScale * viewport->DpiScale;
48334864
if (viewport->GetMainRect().Overlaps(ImRect(g.IO.MousePos, g.IO.MousePos + ImVec2(mouse_cursor_size.x + 2, mouse_cursor_size.y + 2) * scale)))
@@ -6723,20 +6754,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
67236754
window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
67246755
PushClipRect(host_rect.Min, host_rect.Max, false);
67256756

6726-
// Draw modal or window list full viewport dimming background (for other viewports we'll render them in EndFrame)
6727-
ImGuiWindow* window_window_list = g.NavWindowingListWindow;
6728-
const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0;
6729-
const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && ((window == g.NavWindowingTargetAnim->RootWindowDockTree) || (window == window_window_list && window_window_list->Viewport != g.NavWindowingTargetAnim->Viewport));
6730-
if (dim_bg_for_modal || dim_bg_for_window_list)
6731-
{
6732-
const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
6733-
if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost))
6734-
window->DrawList->ChannelsSetCurrent(0);
6735-
window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col);
6736-
if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost))
6737-
window->DrawList->ChannelsSetCurrent(1);
6738-
}
6739-
67406757
// Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71)
67416758
// When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
67426759
// 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)
@@ -8985,6 +9002,16 @@ ImGuiWindow* ImGui::GetTopMostPopupModal()
89859002
return NULL;
89869003
}
89879004

9005+
ImGuiWindow* ImGui::GetTopMostAndVisiblePopupModal()
9006+
{
9007+
ImGuiContext& g = *GImGui;
9008+
for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
9009+
if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
9010+
if ((popup->Flags & ImGuiWindowFlags_Modal) && IsWindowActiveAndVisible(popup))
9011+
return popup;
9012+
return NULL;
9013+
}
9014+
89889015
void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags)
89899016
{
89909017
ImGuiContext& g = *GImGui;

imgui_internal.h

+1
Original file line numberDiff line numberDiff line change
@@ -2772,6 +2772,7 @@ namespace ImGui
27722772
IMGUI_API void BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags);
27732773
IMGUI_API ImRect GetPopupAllowedExtentRect(ImGuiWindow* window);
27742774
IMGUI_API ImGuiWindow* GetTopMostPopupModal();
2775+
IMGUI_API ImGuiWindow* GetTopMostAndVisiblePopupModal();
27752776
IMGUI_API ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window);
27762777
IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy);
27772778

0 commit comments

Comments
 (0)