From f14042ca786324dcdf54a4921438c1e4d8320898 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 10 Feb 2021 14:54:59 +0100 Subject: [PATCH] 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. --- docs/CHANGELOG.txt | 19 ++- imgui.cpp | 362 ++++++++++++++++++++++++++++++++++----------- imgui.h | 61 +++++++- imgui_demo.cpp | 47 ++++-- imgui_internal.h | 47 ++++-- imgui_widgets.cpp | 29 +++- 6 files changed, 443 insertions(+), 122 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 3913f5cf8..23a997018 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -52,6 +52,21 @@ Breaking Changes: Other Changes: +- Viewports Added ImGui::GetMainViewport() as a way to get the bounds and work area of the host display. (#3789, #1542) + - In 'master' branch or without multi-viewports feature enabled: + - GetMainViewport()->Pos is always == (0,0) + - GetMainViewport()->Size is always == io.DisplaySize + - In 'docking' branch and with the multi-viewports feature enabled: + - GetMainViewport() will return information from your host Platform Window. + - In the future, we will support a "no main viewport" mode and this may return bounds of your main monitor. + - For forward compatibility with multi-viewports/multi-monitors: + - Code using (0,0) as a way to signify "upper-left of the host window" should use GetMainViewport()->Pos. + - Code using io.DisplaySize as a way to signify "size of the host window" should use GetMainViewport()->Size. + - We are also exposing a work area in ImGuiViewport ('WorkPos', 'WorkSize' vs 'Pos', 'Size' for full area): + - For a Platform Window, the work area is generally the full area minus space used by menu-bars. + - For a Platform Monitor, the work area is generally the full area minus space used by task-bars. + - All of this has been the case in 'docking' branch for a long time. What we've done is merely merging + a small chunk of the multi-viewport logic into 'master' to standardize some concepts ahead of time. - Tables: Fixed PopItemWidth() or multi-components items not restoring per-colum ItemWidth correctly. (#3760) - Window: Fixed minor title bar text clipping issue when FramePadding is small/zero and there are no close button in the window. (#3731) @@ -80,7 +95,9 @@ Other Changes: Would lead to a buffer read overflow. - ImDrawList: Clarified PathArcTo() need for a_min <= a_max with an assert. - ImDrawList: Fixed PathArcToFast() handling of a_min > a_max. -- Demo: Added 'Examples->Fullscreen Window' demo. (#3789) +- Metrics: Backported "Viewports" debug visualizer from 'docking' branch. +- Demo: Added 'Examples->Fullscreen Window' demo using GetMainViewport() values. (#3789) +- Demo: 'Simple Overlay' demo now moves under main menu-bar (if any) using GetMainViewport()'s work area. - Backends: Win32: Dynamically loading XInput DLL instead of linking with it, facilite compiling with old WindowSDK versions or running on Windows 7. (#3646, #3645, #3248, #2716) [@Demonese] - Backends: Vulkan: Add support for custom Vulkan function loader and VK_NO_PROTOTYPES. (#3759, #3227) [@Hossein-Noroozpour] diff --git a/imgui.cpp b/imgui.cpp index 48b6f8ca7..a68aa4e89 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -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* out_list, ImDrawList* draw_list); static void AddWindowToSortBuffer(ImVector* 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* out_list, ImDrawList* d out_list->push_back(draw_list); } -static void AddWindowToDrawData(ImVector* 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* draw_lists, ImDrawData* draw_data) +static void SetupViewportDrawData(ImGuiViewportP* viewport, ImVector* 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* windows, const char* label) { if (!TreeNode(label, "%s (%d)", label, windows->Size)) @@ -11182,12 +11375,13 @@ void ImGui::DebugNodeWindowsList(ImVector* 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*, const char*) {} +void ImGui::DebugNodeViewport(ImGuiViewportP*) {} #endif diff --git a/imgui.h b/imgui.h index 627fc37ae..103616461 100644 --- a/imgui.h +++ b/imgui.h @@ -31,6 +31,7 @@ Index of this file: // [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, ImColor) // [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawListFlags, ImDrawList, ImDrawData) // [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont) +// [SECTION] Viewports (ImGuiViewportFlags, ImGuiViewport) */ @@ -59,7 +60,7 @@ 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.81 WIP" -#define IMGUI_VERSION_NUM 18003 +#define IMGUI_VERSION_NUM 18004 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -143,6 +144,7 @@ struct ImGuiTableSortSpecs; // Sorting specifications for a table (often struct ImGuiTableColumnSortSpecs; // Sorting specification for one column of a table struct ImGuiTextBuffer; // Helper to hold and append into a text buffer (~string builder) struct ImGuiTextFilter; // Helper to parse and apply text filters (e.g. "aaaaa[,bbbbb][,ccccc]") +struct ImGuiViewport; // A Platform Window (always only one in 'master' branch), in the future may represent Platform Monitor // Enums/Flags (declared as int for compatibility with old C++, to allow using as flags 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! @@ -181,6 +183,7 @@ typedef int ImGuiTableFlags; // -> enum ImGuiTableFlags_ // Flags: F typedef int ImGuiTableColumnFlags; // -> enum ImGuiTableColumnFlags_// Flags: For TableSetupColumn() typedef int ImGuiTableRowFlags; // -> enum ImGuiTableRowFlags_ // Flags: For TableNextRow() typedef int ImGuiTreeNodeFlags; // -> enum ImGuiTreeNodeFlags_ // Flags: for TreeNode(), TreeNodeEx(), CollapsingHeader() +typedef int ImGuiViewportFlags; // -> enum ImGuiViewportFlags_ // Flags: for ImGuiViewport typedef int ImGuiWindowFlags; // -> enum ImGuiWindowFlags_ // Flags: for Begin(), BeginChild() // Other types @@ -414,8 +417,8 @@ namespace ImGui IMGUI_API void SetCursorPosX(float local_x); // GetWindowPos() + GetCursorPos() == GetCursorScreenPos() etc.) IMGUI_API void SetCursorPosY(float local_y); // IMGUI_API ImVec2 GetCursorStartPos(); // initial cursor position in window coordinates - IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position in absolute screen coordinates [0..io.DisplaySize] (useful to work with ImDrawList API) - IMGUI_API void SetCursorScreenPos(const ImVec2& pos); // cursor position in absolute screen coordinates [0..io.DisplaySize] + IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position in absolute coordinates (useful to work with ImDrawList API). generally top-left == GetMainViewport()->Pos == (0,0) in single viewport mode, and bottom-right == GetMainViewport()->Pos+Size == io.DisplaySize in single-viewport mode. + IMGUI_API void SetCursorScreenPos(const ImVec2& pos); // cursor position in absolute coordinates IMGUI_API void AlignTextToFramePadding(); // vertically align upcoming text baseline to FramePadding.y so that it will align properly to regularly framed items (call if you have text on a line before a framed item) IMGUI_API float GetTextLineHeight(); // ~ FontSize IMGUI_API float GetTextLineHeightWithSpacing(); // ~ FontSize + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of text) @@ -787,6 +790,12 @@ namespace ImGui IMGUI_API ImVec2 GetItemRectSize(); // get size of last item IMGUI_API void SetItemAllowOverlap(); // allow last item to be overlapped by a subsequent item. sometimes useful with invisible buttons, selectables, etc. to catch unused area. + // Viewports + // - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows. + // - In 'docking' branch with multi-viewport enabled, we extend this concept to have multiple active viewports. + // - In the future we will extend this concept further to also represent Platform Monitor and support a "no main platform window" operation mode. + IMGUI_API ImGuiViewport* GetMainViewport(); // return primary/default viewport. + // Miscellaneous Utilities IMGUI_API bool IsRectVisible(const ImVec2& size); // test if rectangle (of given size, starting from cursor position) is visible / not clipped. IMGUI_API bool IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max); // test if rectangle (in screen space) is visible / not clipped. to perform coarse clipping on user's side. @@ -1736,7 +1745,7 @@ struct ImGuiIO ImGuiConfigFlags ConfigFlags; // = 0 // See ImGuiConfigFlags_ enum. Set by user/application. Gamepad/keyboard navigation options, etc. ImGuiBackendFlags BackendFlags; // = 0 // See ImGuiBackendFlags_ enum. Set by backend (imgui_impl_xxx files or custom backend) to communicate features supported by the backend. - ImVec2 DisplaySize; // // Main display size, in pixels. + ImVec2 DisplaySize; // // Main display size, in pixels (generally == GetMainViewport()->Size) float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds. float IniSavingRate; // = 5.0f // Minimum time between saving positions/sizes to .ini file, in seconds. const char* IniFilename; // = "imgui.ini" // Path to .ini file. Set NULL to disable automatic .ini loading/saving, if e.g. you want to manually load/save from memory. @@ -2334,7 +2343,8 @@ enum ImDrawListFlags_ // Each dear imgui window contains its own ImDrawList. You can use ImGui::GetWindowDrawList() to // access the current window draw list and draw custom primitives. // You can interleave normal ImGui:: calls and adding primitives to the current draw list. -// All positions are generally in pixel coordinates (top-left at (0,0), bottom-right at io.DisplaySize), but you are totally free to apply whatever transformation matrix to want to the data (if you apply such transformation you'll want to apply it to ClipRect as well) +// In single viewport mode, top-left is == GetMainViewport()->Pos (generally 0,0), bottom-right is == GetMainViewport()->Pos+Size (generally io.DisplaySize). +// You are totally free to apply whatever transformation matrix to want to the data (depending on the use of the transformation you may want to apply it to ClipRect as well!) // Important: Primitives are always added to the list and not culled (culling is done at higher-level by ImGui:: functions), if you use this API a lot consider coarse culling your drawn objects. struct ImDrawList { @@ -2465,8 +2475,8 @@ struct ImDrawData int TotalIdxCount; // For convenience, sum of all ImDrawList's IdxBuffer.Size int TotalVtxCount; // For convenience, sum of all ImDrawList's VtxBuffer.Size ImDrawList** CmdLists; // Array of ImDrawList* to render. The ImDrawList are owned by ImGuiContext and only pointed to from here. - ImVec2 DisplayPos; // Upper-left position of the viewport to render (== upper-left of the orthogonal projection matrix to use) - ImVec2 DisplaySize; // Size of the viewport to render (== io.DisplaySize for the main viewport) (DisplayPos + DisplaySize == lower-right of the orthogonal projection matrix to use) + ImVec2 DisplayPos; // Top-left position of the viewport to render (== top-left of the orthogonal projection matrix to use) (== GetMainViewport()->Pos for the main viewport, == (0.0) in most single-viewport applications) + ImVec2 DisplaySize; // Size of the viewport to render (== GetMainViewport()->Size for the main viewport, == io.DisplaySize in most single-viewport applications) ImVec2 FramebufferScale; // Amount of pixels for each unit of DisplaySize. Based on io.DisplayFramebufferScale. Generally (1,1) on normal display, (2,2) on OSX with Retina display. // Functions @@ -2724,6 +2734,43 @@ struct ImFont IMGUI_API bool IsGlyphRangeUnused(unsigned int c_begin, unsigned int c_last); }; +//----------------------------------------------------------------------------- +// [SECTION] Viewports +//----------------------------------------------------------------------------- + +// Flags stored in ImGuiViewport::Flags +enum ImGuiViewportFlags_ +{ + ImGuiViewportFlags_None = 0, + ImGuiViewportFlags_IsPlatformWindow = 1 << 0, // Represent a Platform Window + ImGuiViewportFlags_IsPlatformMonitor = 1 << 1, // Represent a Platform Monitor (unused yet) + ImGuiViewportFlags_OwnedByApp = 1 << 2 // Platform Window: is created/managed by the application (rather than a dear imgui backend) +}; + +// - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows. +// - In 'docking' branch with multi-viewport enabled, we extend this concept to have multiple active viewports. +// - In the future we will extend this concept further to also represent Platform Monitor and support a "no main platform window" operation mode. +// - About Main Area vs Work Area: +// - Main Area = entire viewport. +// - Work Area = entire viewport minus sections used by main menu bars (for platform windows), or by task bar (for platform monitor). +// - Windows are generally trying to stay within the Work Area of their host viewport. +struct ImGuiViewport +{ + ImGuiViewportFlags Flags; // See ImGuiViewportFlags_ + ImVec2 Pos; // Main Area: Position of the viewport (Dear Imgui coordinates are the same as OS desktop/native coordinates) + ImVec2 Size; // Main Area: Size of the viewport. + ImVec2 WorkPos; // Work Area: Position of the viewport minus task bars, menus bars, status bars (>= Pos) + ImVec2 WorkSize; // Work Area: Size of the viewport minus task bars, menu bars, status bars (<= Size) + + ImGuiViewport() { memset(this, 0, sizeof(*this)); } + + // Helpers + ImVec2 GetCenter() const { return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } + ImVec2 GetWorkCenter() const { return ImVec2(WorkPos.x + WorkSize.x * 0.5f, WorkPos.y + WorkSize.y * 0.5f); } +}; + +//----------------------------------------------------------------------------- + #if defined(__clang__) #pragma clang diagnostic pop #elif defined(__GNUC__) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 91b39d7ac..de5fe8da3 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -253,6 +253,7 @@ void ImGui::ShowDemoWindow(bool* p_open) // Examples Apps (accessible from the "Examples" menu) static bool show_app_main_menu_bar = false; static bool show_app_documents = false; + static bool show_app_console = false; static bool show_app_log = false; static bool show_app_layout = false; @@ -320,7 +321,8 @@ void ImGui::ShowDemoWindow(bool* p_open) // We specify a default position/size in case there's no data in the .ini file. // We only do it to make the demo applications a little more welcoming, but typically this isn't required. - ImGui::SetNextWindowPos(ImVec2(650, 20), ImGuiCond_FirstUseEver); + const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ImGui::SetNextWindowPos(ImVec2(main_viewport->WorkPos.x + 650, main_viewport->WorkPos.y + 20), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(550, 680), ImGuiCond_FirstUseEver); // Main body of the Demo window starts here. @@ -3231,7 +3233,7 @@ static void ShowDemoWindowPopups() ImGui::OpenPopup("Delete?"); // Always center this window when appearing - ImVec2 center(ImGui::GetIO().DisplaySize.x * 0.5f, ImGui::GetIO().DisplaySize.y * 0.5f); + ImVec2 center = ImGui::GetMainViewport()->GetCenter(); ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); if (ImGui::BeginPopupModal("Delete?", NULL, ImGuiWindowFlags_AlwaysAutoResize)) @@ -6983,16 +6985,22 @@ static void ShowExampleAppConstrainedResize(bool* p_open) // + a context-menu to choose which corner of the screen to use. static void ShowExampleAppSimpleOverlay(bool* p_open) { - const float DISTANCE = 10.0f; + const float PAD = 10.0f; static int corner = 0; ImGuiIO& io = ImGui::GetIO(); ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav; if (corner != -1) { - window_flags |= ImGuiWindowFlags_NoMove; - ImVec2 window_pos = ImVec2((corner & 1) ? io.DisplaySize.x - DISTANCE : DISTANCE, (corner & 2) ? io.DisplaySize.y - DISTANCE : DISTANCE); - ImVec2 window_pos_pivot = ImVec2((corner & 1) ? 1.0f : 0.0f, (corner & 2) ? 1.0f : 0.0f); + const ImGuiViewport* viewport = ImGui::GetMainViewport(); + ImVec2 work_pos = viewport->WorkPos; // Use work area to avoid menu-bar/task-bar, if any! + ImVec2 work_size = viewport->WorkSize; + ImVec2 window_pos, window_pos_pivot; + window_pos.x = (corner & 1) ? (work_pos.x + work_size.x - PAD) : (work_pos.x + PAD); + window_pos.y = (corner & 2) ? (work_pos.y + work_size.y - PAD) : (work_pos.y + PAD); + window_pos_pivot.x = (corner & 1) ? 1.0f : 0.0f; + window_pos_pivot.y = (corner & 2) ? 1.0f : 0.0f; ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot); + window_flags |= ImGuiWindowFlags_NoMove; } ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background if (ImGui::Begin("Example: Simple overlay", p_open, window_flags)) @@ -7024,12 +7032,21 @@ static void ShowExampleAppSimpleOverlay(bool* p_open) // Demonstrate creating a window covering the entire screen/viewport static void ShowExampleAppFullscreen(bool* p_open) { - ImGuiIO& io = ImGui::GetIO(); + static bool use_work_area = true; static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings; - ImGui::SetNextWindowPos(ImVec2(0, 0)); - ImGui::SetNextWindowSize(io.DisplaySize); + + // We demonstrate using the full viewport area or the work area (without menu-bars, task-bars etc.) + // Based on your use case you may want one of the other. + const ImGuiViewport* viewport = ImGui::GetMainViewport(); + ImGui::SetNextWindowPos(use_work_area ? viewport->WorkPos : viewport->Pos); + ImGui::SetNextWindowSize(use_work_area ? viewport->WorkSize : viewport->Size); + if (ImGui::Begin("Example: Fullscreen window", p_open, flags)) { + ImGui::Checkbox("Use work area instead of main area", &use_work_area); + ImGui::SameLine(); + HelpMarker("Main Area = entire viewport,\nWork Area = entire viewport minus sections used by the main menu bars, task bars etc.\n\nEnable the main-menu bar in Examples menu to see the difference."); + ImGui::CheckboxFlags("ImGuiWindowFlags_NoBackground", &flags, ImGuiWindowFlags_NoBackground); ImGui::CheckboxFlags("ImGuiWindowFlags_NoDecoration", &flags, ImGuiWindowFlags_NoDecoration); ImGui::Indent(); @@ -7037,6 +7054,9 @@ static void ShowExampleAppFullscreen(bool* p_open) ImGui::CheckboxFlags("ImGuiWindowFlags_NoCollapse", &flags, ImGuiWindowFlags_NoCollapse); ImGui::CheckboxFlags("ImGuiWindowFlags_NoScrollbar", &flags, ImGuiWindowFlags_NoScrollbar); ImGui::Unindent(); + + if (p_open && ImGui::Button("Close this window")) + *p_open = false; } ImGui::End(); } @@ -7050,16 +7070,19 @@ static void ShowExampleAppFullscreen(bool* p_open) // Read FAQ section "How can I have multiple widgets with the same label?" for details. static void ShowExampleAppWindowTitles(bool*) { + const ImGuiViewport* viewport = ImGui::GetMainViewport(); + const ImVec2 base_pos = viewport->Pos; + // By default, Windows are uniquely identified by their title. // You can use the "##" and "###" markers to manipulate the display/ID. // Using "##" to display same title but have unique identifier. - ImGui::SetNextWindowPos(ImVec2(100, 100), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 100), ImGuiCond_FirstUseEver); ImGui::Begin("Same title as another window##1"); ImGui::Text("This is window 1.\nMy title is the same as window 2, but my identifier is unique."); ImGui::End(); - ImGui::SetNextWindowPos(ImVec2(100, 200), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 200), ImGuiCond_FirstUseEver); ImGui::Begin("Same title as another window##2"); ImGui::Text("This is window 2.\nMy title is the same as window 1, but my identifier is unique."); ImGui::End(); @@ -7067,7 +7090,7 @@ static void ShowExampleAppWindowTitles(bool*) // Using "###" to display a changing title but keep a static identifier "AnimatedTitle" char buf[128]; sprintf(buf, "Animated title %c %d###AnimatedTitle", "|/-\\"[(int)(ImGui::GetTime() / 0.25f) & 3], ImGui::GetFrameCount()); - ImGui::SetNextWindowPos(ImVec2(100, 300), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 300), ImGuiCond_FirstUseEver); ImGui::Begin(buf); ImGui::Text("This window has a changing title."); ImGui::End(); diff --git a/imgui_internal.h b/imgui_internal.h index 16e5b16a1..f69c10033 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -652,8 +652,9 @@ struct ImDrawDataBuilder { ImVector Layers[2]; // Global layers for: regular, tooltip - void Clear() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].resize(0); } - void ClearFreeMemory() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].clear(); } + void Clear() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].resize(0); } + void ClearFreeMemory() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].clear(); } + int GetDrawListCount() const { int count = 0; for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) count += Layers[n].Size; return count; } IMGUI_API void FlattenIntoSingleLayer(); }; @@ -1146,9 +1147,26 @@ struct ImGuiOldColumns // [SECTION] Viewport support //----------------------------------------------------------------------------- -#ifdef IMGUI_HAS_VIEWPORT -// -#endif // #ifdef IMGUI_HAS_VIEWPORT +// ImGuiViewport Private/Internals fields (cardinal sin: we are using inheritance!) +// Every instance of ImGuiViewport is in fact a ImGuiViewportP. +struct ImGuiViewportP : public ImGuiViewport +{ + int DrawListsLastFrame[2]; // Last frame number the background (0) and foreground (1) draw lists were used + ImDrawList* DrawLists[2]; // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays. + ImDrawData DrawDataP; + ImDrawDataBuilder DrawDataBuilder; + + ImVec2 WorkOffsetMin; // Work Area: Offset from Pos to top-left corner of Work Area. Generally (0,0) or (0,+main_menu_bar_height). Work Area is Full Area but without menu-bars/status-bars (so WorkArea always fit inside Pos/Size!) + ImVec2 WorkOffsetMax; // Work Area: Offset from Pos+Size to bottom-right corner of Work Area. Generally (0,0) or (0,-status_bar_height). + ImVec2 CurrWorkOffsetMin; // Work Area: Offset being built/increased during current frame + ImVec2 CurrWorkOffsetMax; // Work Area: Offset being built/decreased during current frame + + ImGuiViewportP() { DrawListsLastFrame[0] = DrawListsLastFrame[1] = -1; DrawLists[0] = DrawLists[1] = NULL; } + ~ImGuiViewportP() { if (DrawLists[0]) IM_DELETE(DrawLists[0]); if (DrawLists[1]) IM_DELETE(DrawLists[1]); } + ImRect GetMainRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } + ImRect GetWorkRect() const { return ImRect(WorkPos.x, WorkPos.y, WorkPos.x + WorkSize.x, WorkPos.y + WorkSize.y); } + void UpdateWorkRect() { WorkPos = ImVec2(Pos.x + WorkOffsetMin.x, Pos.y + WorkOffsetMin.y); WorkSize = ImVec2(ImMax(0.0f, Size.x - WorkOffsetMin.x + WorkOffsetMax.x), ImMax(0.0f, Size.y - WorkOffsetMin.y + WorkOffsetMax.y)); } +}; //----------------------------------------------------------------------------- // [SECTION] Settings support @@ -1332,6 +1350,9 @@ struct ImGuiContext ImVectorOpenPopupStack; // Which popups are open (persistent) ImVectorBeginPopupStack; // Which level of BeginPopup() we are in (reset every frame) + // Viewports + ImVector Viewports; // Active viewports (Size==1 in 'master' branch). Each viewports hold their copy of ImDrawData. + // Gamepad/keyboard Navigation ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusWindow' ImGuiID NavId; // Focused item for navigation @@ -1389,11 +1410,7 @@ struct ImGuiContext bool FocusTabPressed; // // Render - ImDrawData DrawData; // Main ImDrawData instance to pass render information to the user - ImDrawDataBuilder DrawDataBuilder; float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) - ImDrawList BackgroundDrawList; // First draw list to be rendered. - ImDrawList ForegroundDrawList; // Last draw list to be rendered. This is where we the render software mouse cursor (if io.MouseDrawCursor is set) and most debug overlays. ImGuiMouseCursor MouseCursor; // Drag and Drop @@ -1491,7 +1508,7 @@ struct ImGuiContext int WantTextInputNextFrame; char TempBuffer[1024 * 3 + 1]; // Temporary text buffer - ImGuiContext(ImFontAtlas* shared_font_atlas) : BackgroundDrawList(&DrawListSharedData), ForegroundDrawList(&DrawListSharedData) + ImGuiContext(ImFontAtlas* shared_font_atlas) { Initialized = false; FontAtlasOwnedByContext = shared_font_atlas ? false : true; @@ -1580,8 +1597,6 @@ struct ImGuiContext FocusTabPressed = false; DimBgRatio = 0.0f; - BackgroundDrawList._OwnerName = "##Background"; // Give it a name for debugging - ForegroundDrawList._OwnerName = "##Foreground"; // Give it a name for debugging MouseCursor = ImGuiMouseCursor_Arrow; DragDropActive = DragDropWithinSource = DragDropWithinTarget = false; @@ -2185,7 +2200,9 @@ namespace ImGui // Fonts, drawing IMGUI_API void SetCurrentFont(ImFont* font); inline ImFont* GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; } - inline ImDrawList* GetForegroundDrawList(ImGuiWindow* window) { IM_UNUSED(window); ImGuiContext& g = *GImGui; return &g.ForegroundDrawList; } // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'docking' branches. + inline ImDrawList* GetForegroundDrawList(ImGuiWindow* window) { IM_UNUSED(window); return GetForegroundDrawList(); } // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'docking' branches. + IMGUI_API ImDrawList* GetBackgroundDrawList(ImGuiViewport* viewport); // get background draw list for the given viewport. this draw list will be the first rendering one. Useful to quickly draw shapes/text behind dear imgui contents. + IMGUI_API ImDrawList* GetForegroundDrawList(ImGuiViewport* viewport); // get foreground draw list for the given viewport. this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents. // Init IMGUI_API void Initialize(ImGuiContext* context); @@ -2486,7 +2503,7 @@ namespace ImGui IMGUI_API void DebugNodeColumns(ImGuiOldColumns* columns); IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, const char* label); - IMGUI_API void DebugNodeDrawCmdShowMeshAndBoundingBox(ImGuiWindow* window, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb); + IMGUI_API void DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb); IMGUI_API void DebugNodeStorage(ImGuiStorage* storage, const char* label); IMGUI_API void DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label); IMGUI_API void DebugNodeTable(ImGuiTable* table); @@ -2494,6 +2511,8 @@ namespace ImGui IMGUI_API void DebugNodeWindow(ImGuiWindow* window, const char* label); IMGUI_API void DebugNodeWindowSettings(ImGuiWindowSettings* settings); IMGUI_API void DebugNodeWindowsList(ImVector* windows, const char* label); + IMGUI_API void DebugNodeViewport(ImGuiViewportP* viewport); + IMGUI_API void DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb); } // namespace ImGui diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index cabdd5fd5..e061694e4 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6569,18 +6569,39 @@ void ImGui::EndMenuBar() window->DC.MenuBarAppending = false; } -// For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set. bool ImGui::BeginMainMenuBar() { ImGuiContext& g = *GImGui; + ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport(); + ImGuiWindow* menu_bar_window = FindWindowByName("##MainMenuBar"); + + // For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set. g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f)); - SetNextWindowPos(ImVec2(0.0f, 0.0f)); - SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.NextWindowData.MenuBarOffsetMinVal.y + g.FontBaseSize + g.Style.FramePadding.y)); + + // Get our rectangle at the top of the work area + if (menu_bar_window == NULL || menu_bar_window->BeginCount == 0) + { + // Set window position + // We don't attempt to calculate our height ahead, as it depends on the per-viewport font size. + // However menu-bar will affect the minimum window size so we'll get the right height. + ImVec2 menu_bar_pos = viewport->Pos + viewport->CurrWorkOffsetMin; + ImVec2 menu_bar_size = ImVec2(viewport->Size.x - viewport->CurrWorkOffsetMin.x + viewport->CurrWorkOffsetMax.x, 1.0f); + SetNextWindowPos(menu_bar_pos); + SetNextWindowSize(menu_bar_size); + } + + // Create window PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); - PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); + PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); // Lift normal size constraint, however the presence of a menu-bar will give us the minimum height we want. ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar; bool is_open = Begin("##MainMenuBar", NULL, window_flags) && BeginMenuBar(); PopStyleVar(2); + + // Report our size into work area (for next frame) using actual window size + menu_bar_window = GetCurrentWindow(); + if (menu_bar_window->BeginCount == 1) + viewport->CurrWorkOffsetMin.y += menu_bar_window->Size.y; + g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f); if (!is_open) {