Viewports Added ImGui::GetMainViewport() as a way to get the bounds and work area of the host display. (#3789, #1542)

Viewports: Backport a few constructs from 'docking' branch.
Viewports: Added ImGuiViewportFlags_IsPlatformWindow, ImGuiViewportFlags_IsPlatformMonitor, ImGuiViewportFlags_OwnedByApp (none of them were in docking branch yet). ImGuiViewportFlags_IsPlatformMonitor is merely there to convey future intent, not yet used.
Reduce uses of io.DisplaySize.
MainMenuBar: Backport work area handling code from 'docking' branch.
Metrics: Backported "Viewports" debug visualizer from 'docking' branch.
Demo: Rework 'Examples->Fullscreen Window'.
Demo: 'Simple Overlay' demo now moves under main menu-bar (if any) using GetMainViewport()'s work area.
This commit is contained in:
ocornut
2021-02-10 14:54:59 +01:00
parent 3b4d3a9b73
commit f14042ca78
6 changed files with 443 additions and 122 deletions

362
imgui.cpp
View File

@ -75,6 +75,7 @@ CODE
// [SECTION] DRAG AND DROP
// [SECTION] LOGGING/CAPTURING
// [SECTION] SETTINGS
// [SECTION] VIEWPORTS
// [SECTION] PLATFORM DEPENDENT HELPERS
// [SECTION] METRICS/DEBUGGER WINDOW
@ -309,10 +310,11 @@ CODE
// We are using scissoring to clip some objects. All low-level graphics API should supports it.
// - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
// (some elements visible outside their bounds) but you can fix that once everything else works!
// - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize)
// In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize.
// However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github),
// always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
// - Clipping coordinates are provided in imgui coordinates space:
// - For a given viewport, draw_data->DisplayPos == viewport->Pos and draw_data->DisplaySize == viewport->Size
// - In a single viewport application, draw_data->DisplayPos == (0,0) and draw_data->DisplaySize == io.DisplaySize, but always use GetMainViewport()->Pos/Size instead of hardcoding those values.
// - In the interest of supporting multi-viewport applications (see 'docking' branch on github),
// always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
// - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
ImVec2 pos = draw_data->DisplayPos;
MyEngineScissor((int)(pcmd->ClipRect.x - pos.x), (int)(pcmd->ClipRect.y - pos.y), (int)(pcmd->ClipRect.z - pos.x), (int)(pcmd->ClipRect.w - pos.y));
@ -852,8 +854,6 @@ static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* wind
static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
static ImRect GetViewportRect();
// Settings
static void WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
@ -900,6 +900,9 @@ static void RenderWindowOuterBorders(ImGuiWindow* window);
static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size);
static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
// Viewports
static void UpdateViewportsNewFrame();
}
//-----------------------------------------------------------------------------
@ -3391,7 +3394,8 @@ ImGuiIO& ImGui::GetIO()
ImDrawData* ImGui::GetDrawData()
{
ImGuiContext& g = *GImGui;
return g.DrawData.Valid ? &g.DrawData : NULL;
ImGuiViewportP* viewport = g.Viewports[0];
return viewport->DrawDataP.Valid ? &viewport->DrawDataP : NULL;
}
double ImGui::GetTime()
@ -3404,14 +3408,50 @@ int ImGui::GetFrameCount()
return GImGui->FrameCount;
}
static ImDrawList* GetViewportDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name)
{
// Create the draw list on demand, because they are not frequently used for all viewports
ImGuiContext& g = *GImGui;
IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->DrawLists));
ImDrawList* draw_list = viewport->DrawLists[drawlist_no];
if (draw_list == NULL)
{
draw_list = IM_NEW(ImDrawList)(&g.DrawListSharedData);
draw_list->_OwnerName = drawlist_name;
viewport->DrawLists[drawlist_no] = draw_list;
}
// Our ImDrawList system requires that there is always a command
if (viewport->DrawListsLastFrame[drawlist_no] != g.FrameCount)
{
draw_list->_ResetForNewFrame();
draw_list->PushTextureID(g.IO.Fonts->TexID);
draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false);
viewport->DrawListsLastFrame[drawlist_no] = g.FrameCount;
}
return draw_list;
}
ImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport)
{
return GetViewportDrawList((ImGuiViewportP*)viewport, 0, "##Background");
}
ImDrawList* ImGui::GetBackgroundDrawList()
{
return &GImGui->BackgroundDrawList;
ImGuiContext& g = *GImGui;
return GetBackgroundDrawList(g.Viewports[0]);
}
ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport)
{
return GetViewportDrawList((ImGuiViewportP*)viewport, 1, "##Foreground");
}
ImDrawList* ImGui::GetForegroundDrawList()
{
return &GImGui->ForegroundDrawList;
ImGuiContext& g = *GImGui;
return GetForegroundDrawList(g.Viewports[0]);
}
ImDrawListSharedData* ImGui::GetDrawListSharedData()
@ -3493,7 +3533,8 @@ void ImGui::UpdateMouseMovingWindowEndFrame()
if (g.NavWindow && g.NavWindow->Appearing)
return;
// Click on empty space to focus window and start moving (after we're done with all our widgets)
// Click on empty space to focus window and start moving
// (after we're done with all our widgets)
if (g.IO.MouseClicked[0])
{
// Handle the edge case of a popup being closed while clicking in its empty space.
@ -3826,11 +3867,16 @@ void ImGui::NewFrame()
g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX;
UpdateViewportsNewFrame();
// Setup current font and draw list shared data
g.IO.Fonts->Locked = true;
SetCurrentFont(GetDefaultFont());
IM_ASSERT(g.Font->IsLoaded());
g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
for (int n = 0; n < g.Viewports.Size; n++)
virtual_space.Add(g.Viewports[n]->GetMainRect());
g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4();
g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
g.DrawListSharedData.SetCircleSegmentMaxError(g.Style.CircleSegmentMaxError);
g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
@ -3843,16 +3889,12 @@ void ImGui::NewFrame()
if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
g.BackgroundDrawList._ResetForNewFrame();
g.BackgroundDrawList.PushTextureID(g.IO.Fonts->TexID);
g.BackgroundDrawList.PushClipRectFullScreen();
g.ForegroundDrawList._ResetForNewFrame();
g.ForegroundDrawList.PushTextureID(g.IO.Fonts->TexID);
g.ForegroundDrawList.PushClipRectFullScreen();
// Mark rendering data as invalid to prevent user who may have a handle on it to use it.
g.DrawData.Clear();
for (int n = 0; n < g.Viewports.Size; n++)
{
ImGuiViewportP* viewport = g.Viewports[n];
viewport->DrawDataP.Clear();
}
// Drag and drop keep the source ID alive so even if the source disappear our state is consistent
if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
@ -4039,6 +4081,10 @@ void ImGui::Initialize(ImGuiContext* context)
TableSettingsInstallHandler(context);
#endif // #ifdef IMGUI_HAS_TABLE
// Create default viewport
ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)();
g.Viewports.push_back(viewport);
#ifdef IMGUI_HAS_DOCK
#endif // #ifdef IMGUI_HAS_DOCK
@ -4090,9 +4136,10 @@ void ImGui::Shutdown(ImGuiContext* context)
g.FontStack.clear();
g.OpenPopupStack.clear();
g.BeginPopupStack.clear();
g.DrawDataBuilder.ClearFreeMemory();
g.BackgroundDrawList._ClearFreeMemory();
g.ForegroundDrawList._ClearFreeMemory();
for (int i = 0; i < g.Viewports.Size; i++)
IM_DELETE(g.Viewports[i]);
g.Viewports.clear();
g.TabBars.Clear();
g.CurrentTabBarStack.clear();
@ -4187,25 +4234,25 @@ static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* d
out_list->push_back(draw_list);
}
static void AddWindowToDrawData(ImVector<ImDrawList*>* out_render_list, ImGuiWindow* window)
static void AddWindowToDrawData(ImGuiWindow* window, int layer)
{
ImGuiContext& g = *GImGui;
ImGuiViewportP* viewport = g.Viewports[0];
g.IO.MetricsRenderWindows++;
AddDrawListToDrawData(out_render_list, window->DrawList);
AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[layer], window->DrawList);
for (int i = 0; i < window->DC.ChildWindows.Size; i++)
{
ImGuiWindow* child = window->DC.ChildWindows[i];
if (IsWindowActiveAndVisible(child)) // clipped children may have been marked not active
AddWindowToDrawData(out_render_list, child);
if (IsWindowActiveAndVisible(child)) // Clipped children may have been marked not active
AddWindowToDrawData(child, layer);
}
}
// Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)
static void AddRootWindowToDrawData(ImGuiWindow* window)
{
ImGuiContext& g = *GImGui;
int layer = (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0;
AddWindowToDrawData(&g.DrawDataBuilder.Layers[layer], window);
AddWindowToDrawData(window, layer);
}
void ImDrawDataBuilder::FlattenIntoSingleLayer()
@ -4226,15 +4273,16 @@ void ImDrawDataBuilder::FlattenIntoSingleLayer()
}
}
static void SetupDrawData(ImVector<ImDrawList*>* draw_lists, ImDrawData* draw_data)
static void SetupViewportDrawData(ImGuiViewportP* viewport, ImVector<ImDrawList*>* draw_lists)
{
ImGuiIO& io = ImGui::GetIO();
ImDrawData* draw_data = &viewport->DrawDataP;
draw_data->Valid = true;
draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
draw_data->CmdListsCount = draw_lists->Size;
draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
draw_data->DisplayPos = ImVec2(0.0f, 0.0f);
draw_data->DisplaySize = io.DisplaySize;
draw_data->DisplayPos = viewport->Pos;
draw_data->DisplaySize = viewport->Size;
draw_data->FramebufferScale = io.DisplayFramebufferScale;
for (int n = 0; n < draw_lists->Size; n++)
{
@ -4355,13 +4403,17 @@ void ImGui::Render()
EndFrame();
g.FrameCountRendered = g.FrameCount;
g.IO.MetricsRenderWindows = 0;
g.DrawDataBuilder.Clear();
CallContextHooks(&g, ImGuiContextHookType_RenderPre);
// Add background ImDrawList
if (!g.BackgroundDrawList.VtxBuffer.empty())
AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.BackgroundDrawList);
// Add background ImDrawList (for each active viewport)
for (int n = 0; n != g.Viewports.Size; n++)
{
ImGuiViewportP* viewport = g.Viewports[n];
viewport->DrawDataBuilder.Clear();
if (viewport->DrawLists[0] != NULL)
AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport));
}
// Add ImDrawList to render
ImGuiWindow* windows_to_render_top_most[2];
@ -4376,20 +4428,27 @@ void ImGui::Render()
for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++)
if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window
AddRootWindowToDrawData(windows_to_render_top_most[n]);
g.DrawDataBuilder.FlattenIntoSingleLayer();
// Draw software mouse cursor if requested
if (g.IO.MouseDrawCursor)
RenderMouseCursor(&g.ForegroundDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
// Setup ImDrawData structures for end-user
g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0;
for (int n = 0; n < g.Viewports.Size; n++)
{
ImGuiViewportP* viewport = g.Viewports[n];
viewport->DrawDataBuilder.FlattenIntoSingleLayer();
// Add foreground ImDrawList
if (!g.ForegroundDrawList.VtxBuffer.empty())
AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.ForegroundDrawList);
// Draw software mouse cursor if requested by io.MouseDrawCursor flag
if (g.IO.MouseDrawCursor)
RenderMouseCursor(GetForegroundDrawList(viewport), g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
// Setup ImDrawData structure for end-user
SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData);
g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount;
g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount;
// Add foreground ImDrawList (for each active viewport)
if (viewport->DrawLists[1] != NULL)
AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport));
SetupViewportDrawData(viewport, &viewport->DrawDataBuilder.Layers[0]);
ImDrawData* draw_data = &viewport->DrawDataP;
g.IO.MetricsRenderVertices += draw_data->TotalVtxCount;
g.IO.MetricsRenderIndices += draw_data->TotalIdxCount;
}
CallContextHooks(&g, ImGuiContextHookType_RenderPost);
}
@ -4838,12 +4897,6 @@ ImVec2 ImGui::GetItemRectSize()
return window->DC.LastItemRect.GetSize();
}
static ImRect GetViewportRect()
{
ImGuiContext& g = *GImGui;
return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
}
bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags)
{
ImGuiContext& g = *GImGui;
@ -5008,7 +5061,8 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags)
g.WindowsById.SetVoidPtr(window->ID, window);
// Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
window->Pos = ImVec2(60, 60);
const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
window->Pos = main_viewport->Pos + ImVec2(60, 60);
// User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
if (!(flags & ImGuiWindowFlags_NoSavedSettings))
@ -5116,7 +5170,10 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont
ImVec2 size_min = style.WindowMinSize;
if (is_popup || is_menu) // Popups and menus bypass style.WindowMinSize by default, but we give then a non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups)
size_min = ImMin(size_min, ImVec2(4.0f, 4.0f));
ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, g.IO.DisplaySize - style.DisplaySafeAreaPadding * 2.0f));
// FIXME-VIEWPORT-WORKAREA: May want to use GetWorkSize() instead of Size depending on the type of windows?
ImVec2 avail_size = ImGui::GetMainViewport()->Size;
ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, avail_size - style.DisplaySafeAreaPadding * 2.0f));
// When the window cannot fit all contents (either because of constraints, either because screen is too small),
// we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
@ -5877,9 +5934,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// Calculate the range of allowed position for that window (to be movable and visible past safe area padding)
// When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect.
ImRect viewport_rect(GetViewportRect());
ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport();
ImRect viewport_rect(viewport->GetMainRect());
ImRect viewport_work_rect(viewport->GetWorkRect());
ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
ImRect visibility_rect(viewport_rect.Min + visibility_padding, viewport_rect.Max - visibility_padding);
ImRect visibility_rect(viewport_work_rect.Min + visibility_padding, viewport_work_rect.Max - visibility_padding);
// Clamp position/size so window stays visible within its viewport or monitor
// Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
@ -8163,7 +8222,10 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags fla
// (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves)
// FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
{
const ImGuiViewport* viewport = GetMainViewport();
SetNextWindowPos(viewport->GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
}
flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse;
const bool is_open = Begin(name, p_open, flags);
@ -8256,6 +8318,9 @@ bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flag
// r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.)
// r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it.
// (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor
// information are available, it may represent the entire platform monitor from the frame of reference of the current viewport.
// this allows us to have tooltips/popups displayed out of the parent viewport.)
ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
{
ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
@ -8330,11 +8395,13 @@ ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& s
return pos;
}
// Note that this is used for popups, which can overlap the non work-area of individual viewports.
ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window)
{
ImGuiContext& g = *GImGui;
IM_UNUSED(window);
ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding;
ImRect r_screen = GetViewportRect();
ImRect r_screen = ((ImGuiViewportP*)(void*)GetMainViewport())->GetMainRect();
ImVec2 padding = g.Style.DisplaySafeAreaPadding;
r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
return r_screen;
}
@ -8778,8 +8845,8 @@ static ImVec2 ImGui::NavCalcPreferredRefPos()
// When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item.
const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight()));
ImRect visible_rect = GetViewportRect();
return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta.
ImGuiViewport* viewport = GetMainViewport();
return ImFloor(ImClamp(pos, viewport->Pos, viewport->Pos + viewport->Size)); // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta.
}
}
@ -9082,7 +9149,7 @@ static void ImGui::NavUpdate()
// 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] : ImRect(0, 0, 0, 0);
g.NavScoringRect = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect();
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;
@ -9512,8 +9579,9 @@ void ImGui::NavUpdateWindowingOverlay()
if (g.NavWindowingListWindow == NULL)
g.NavWindowingListWindow = FindWindowByName("###NavWindowingList");
SetNextWindowSizeConstraints(ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
const ImGuiViewport* viewport = GetMainViewport();
SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
@ -10378,9 +10446,41 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl
//-----------------------------------------------------------------------------
// [SECTION] VIEWPORTS, PLATFORM WINDOWS
//-----------------------------------------------------------------------------
// - GetMainViewport()
// - UpdateViewportsNewFrame() [Internal]
// (this section is more complete in the 'docking' branch)
//-----------------------------------------------------------------------------
// (this section is filled in the 'docking' branch)
ImGuiViewport* ImGui::GetMainViewport()
{
ImGuiContext& g = *GImGui;
return g.Viewports[0];
}
// Update viewports and monitor infos
static void ImGui::UpdateViewportsNewFrame()
{
ImGuiContext& g = *GImGui;
IM_ASSERT(g.Viewports.Size == 1);
// Update main viewport with current platform position.
// FIXME-VIEWPORT: Size is driven by backend/user code for backward-compatibility but we should aim to make this more consistent.
ImGuiViewportP* main_viewport = g.Viewports[0];
main_viewport->Flags = ImGuiViewportFlags_IsPlatformWindow | ImGuiViewportFlags_OwnedByApp;
main_viewport->Pos = ImVec2(0.0f, 0.0f);
main_viewport->Size = g.IO.DisplaySize;
for (int n = 0; n < g.Viewports.Size; n++)
{
ImGuiViewportP* viewport = g.Viewports[n];
// Lock down space taken by menu bars and status bars, reset the offset for fucntions like BeginMainMenuBar() to alter them again.
viewport->WorkOffsetMin = viewport->CurrWorkOffsetMin;
viewport->WorkOffsetMax = viewport->CurrWorkOffsetMax;
viewport->CurrWorkOffsetMin = viewport->CurrWorkOffsetMax = ImVec2(0.0f, 0.0f);
viewport->UpdateWorkRect();
}
}
//-----------------------------------------------------------------------------
// [SECTION] DOCKING
@ -10552,6 +10652,8 @@ static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
//-----------------------------------------------------------------------------
// [SECTION] METRICS/DEBUGGER WINDOW
//-----------------------------------------------------------------------------
// - RenderViewportThumbnail() [Internal]
// - RenderViewportsThumbnails() [Internal]
// - MetricsHelpMarker() [Internal]
// - ShowMetricsWindow()
// - DebugNodeColumns() [Internal]
@ -10559,6 +10661,7 @@ static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
// - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal]
// - DebugNodeStorage() [Internal]
// - DebugNodeTabBar() [Internal]
// - DebugNodeViewport() [Internal]
// - DebugNodeWindow() [Internal]
// - DebugNodeWindowSettings() [Internal]
// - DebugNodeWindowsList() [Internal]
@ -10566,6 +10669,57 @@ static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
#ifndef IMGUI_DISABLE_METRICS_WINDOW
void ImGui::DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
ImVec2 scale = bb.GetSize() / viewport->Size;
ImVec2 off = bb.Min - viewport->Pos * scale;
float alpha_mul = 1.0f;
window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f));
for (int i = 0; i != g.Windows.Size; i++)
{
ImGuiWindow* thumb_window = g.Windows[i];
if (!thumb_window->WasActive || (thumb_window->Flags & ImGuiWindowFlags_ChildWindow))
continue;
ImRect thumb_r = thumb_window->Rect();
ImRect title_r = thumb_window->TitleBarRect();
thumb_r = ImRect(ImFloor(off + thumb_r.Min * scale), ImFloor(off + thumb_r.Max * scale));
title_r = ImRect(ImFloor(off + title_r.Min * scale), ImFloor(off + ImVec2(title_r.Max.x, title_r.Min.y) * scale) + ImVec2(0,5)); // Exaggerate title bar height
thumb_r.ClipWithFull(bb);
title_r.ClipWithFull(bb);
const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight);
window->DrawList->AddRectFilled(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_WindowBg, alpha_mul));
window->DrawList->AddRectFilled(title_r.Min, title_r.Max, GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg, alpha_mul));
window->DrawList->AddRect(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_Border, alpha_mul));
window->DrawList->AddText(g.Font, g.FontSize * 1.0f, title_r.Min, GetColorU32(ImGuiCol_Text, alpha_mul), thumb_window->Name, FindRenderedTextEnd(thumb_window->Name));
}
draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul));
}
static void RenderViewportsThumbnails()
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
// We don't display full monitor bounds (we could, but it often looks awkward), instead we display just enough to cover all of our viewports.
float SCALE = 1.0f / 8.0f;
ImRect bb_full(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
for (int n = 0; n < g.Viewports.Size; n++)
bb_full.Add(g.Viewports[n]->GetMainRect());
ImVec2 p = window->DC.CursorPos;
ImVec2 off = p - bb_full.Min * SCALE;
for (int n = 0; n < g.Viewports.Size; n++)
{
ImGuiViewportP* viewport = g.Viewports[n];
ImRect viewport_draw_bb(off + (viewport->Pos) * SCALE, off + (viewport->Pos + viewport->Size) * SCALE);
ImGui::DebugRenderViewportThumbnail(window->DrawList, viewport, viewport_draw_bb);
}
ImGui::Dummy(bb_full.GetSize() * SCALE);
}
// Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds.
static void MetricsHelpMarker(const char* desc)
{
@ -10593,7 +10747,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
// Basic info
Text("Dear ImGui %s", ImGui::GetVersion());
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);
@ -10724,13 +10878,34 @@ void ImGui::ShowMetricsWindow(bool* p_open)
TreePop();
}
// Contents
// Windows
DebugNodeWindowsList(&g.Windows, "Windows");
//DebugNodeWindowList(&g.WindowsFocusOrder, "WindowsFocusOrder");
if (TreeNode("DrawLists", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size))
//DebugNodeWindowsList(&g.WindowsFocusOrder, "WindowsFocusOrder");
// DrawLists
int drawlist_count = 0;
for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++)
drawlist_count += g.Viewports[viewport_i]->DrawDataBuilder.GetDrawListCount();
if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count))
{
for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++)
DebugNodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList");
for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++)
{
ImGuiViewportP* viewport = g.Viewports[viewport_i];
for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++)
for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++)
DebugNodeDrawList(NULL, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList");
}
TreePop();
}
// Viewports
if (TreeNode("Viewports", "Viewports (%d)", g.Viewports.Size))
{
Indent(GetTreeNodeToLabelSpacing());
RenderViewportsThumbnails();
Unindent(GetTreeNodeToLabelSpacing());
for (int i = 0; i < g.Viewports.Size; i++)
DebugNodeViewport(g.Viewports[i]);
TreePop();
}
@ -10973,7 +11148,7 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list,
pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
bool pcmd_node_open = TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf);
if (IsItemHovered() && (cfg->ShowDrawCmdMesh || cfg->ShowDrawCmdBoundingBoxes) && fg_draw_list)
DebugNodeDrawCmdShowMeshAndBoundingBox(window, draw_list, pcmd, cfg->ShowDrawCmdMesh, cfg->ShowDrawCmdBoundingBoxes);
DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, cfg->ShowDrawCmdMesh, cfg->ShowDrawCmdBoundingBoxes);
if (!pcmd_node_open)
continue;
@ -10994,7 +11169,7 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list,
ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area);
Selectable(buf);
if (IsItemHovered() && fg_draw_list)
DebugNodeDrawCmdShowMeshAndBoundingBox(window, draw_list, pcmd, true, false);
DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, true, false);
// Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
ImGuiListClipper clipper;
@ -11027,33 +11202,32 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list,
}
// [DEBUG] Display mesh/aabb of a ImDrawCmd
void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImGuiWindow* window, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb)
void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb)
{
IM_ASSERT(show_mesh || show_aabb);
ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list
ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset;
// Draw wire-frame version of all triangles
ImRect clip_rect = draw_cmd->ClipRect;
ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
ImDrawListFlags backup_flags = fg_draw_list->Flags;
fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
ImDrawListFlags backup_flags = out_draw_list->Flags;
out_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
for (unsigned int idx_n = draw_cmd->IdxOffset; idx_n < draw_cmd->IdxOffset + draw_cmd->ElemCount; )
{
ImVec2 triangle[3];
for (int n = 0; n < 3; n++, idx_n++)
vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos));
if (show_mesh)
fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), true, 1.0f); // In yellow: mesh triangles
out_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), true, 1.0f); // In yellow: mesh triangles
}
// Draw bounding boxes
if (show_aabb)
{
fg_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU
fg_draw_list->AddRect(ImFloor(vtxs_rect.Min), ImFloor(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles
out_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU
out_draw_list->AddRect(ImFloor(vtxs_rect.Min), ImFloor(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles
}
fg_draw_list->Flags = backup_flags;
out_draw_list->Flags = backup_flags;
}
// [DEBUG] Display contents of ImGuiStorage
@ -11105,6 +11279,26 @@ void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label)
}
}
void ImGui::DebugNodeViewport(ImGuiViewportP* viewport)
{
SetNextItemOpen(true, ImGuiCond_Once);
if (TreeNode("viewport0", "Viewport #%d", 0))
{
ImGuiWindowFlags flags = viewport->Flags;
BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Offset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f",
viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y,
viewport->WorkOffsetMin.x, viewport->WorkOffsetMin.y, viewport->WorkOffsetMax.x, viewport->WorkOffsetMax.y);
BulletText("Flags: 0x%04X =%s%s%s", viewport->Flags,
(flags & ImGuiViewportFlags_IsPlatformWindow) ? " IsPlatformWindow" : "",
(flags & ImGuiViewportFlags_IsPlatformMonitor) ? " IsPlatformMonitor" : "",
(flags & ImGuiViewportFlags_OwnedByApp) ? " OwnedByApp" : "");
for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++)
for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++)
DebugNodeDrawList(NULL, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList");
TreePop();
}
}
void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)
{
if (window == NULL)
@ -11162,7 +11356,6 @@ void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings* settings)
settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed);
}
void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label)
{
if (!TreeNode(label, "%s (%d)", label, windows->Size))
@ -11182,12 +11375,13 @@ void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* la
void ImGui::ShowMetricsWindow(bool*) {}
void ImGui::DebugNodeColumns(ImGuiOldColumns*) {}
void ImGui::DebugNodeDrawList(ImGuiWindow*, const ImDrawList*, const char*) {}
void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImGuiWindow*, const ImDrawList*, const ImDrawCmd*, bool, bool) {}
void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList*, const ImDrawList*, const ImDrawCmd*, bool, bool) {}
void ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {}
void ImGui::DebugNodeTabBar(ImGuiTabBar*, const char*) {}
void ImGui::DebugNodeWindow(ImGuiWindow*, const char*) {}
void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {}
void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>*, const char*) {}
void ImGui::DebugNodeViewport(ImGuiViewportP*) {}
#endif