From 25f25d546d360a7653772fc712ee20c19f8048d5 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 17 Apr 2018 22:09:01 +0200 Subject: [PATCH] Viewport: Fixed manual mouse resize to handle mouse viewport change, which will allow resizing a window while it changes viewport. Releasing manual resize may merge a window. Added ConvertViewportPosToViewportPos() helper. Renamed NewFrameUpdateMovingWindowDropViewport() to UpdateTryMergeWindowIntoHostViewport() and removed the now-unnecessary half of the function. + renaming (#1542) --- imgui.cpp | 78 ++++++++++++++++++++++++++---------------------- imgui_internal.h | 1 + 2 files changed, 43 insertions(+), 36 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e2727f58..846ae9d7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -753,7 +753,6 @@ static void NavUpdateWindowing(); static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id); static void NewFrameUpdateMovingWindow(); -static void NewFrameUpdateMovingWindowDropViewport(ImGuiWindow* window); static void UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]); static void FocusFrontMostActiveWindow(ImGuiWindow* ignore_window); @@ -761,9 +760,11 @@ static void FocusFrontMostActiveWindow(ImGuiWindow* ignore_window); const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHash("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter. static inline ImVec2 ConvertViewportPosToPlatformPos(const ImVec2& imgui_pos, ImGuiViewport* viewport) { return imgui_pos - viewport->Pos + viewport->PlatformPos; } static inline ImVec2 ConvertPlatformPosToViewportPos(const ImVec2& platform_pos, ImGuiViewport* viewport) { return platform_pos - viewport->PlatformPos + viewport->Pos; } +static inline ImVec2 ConvertViewportPosToViewportPos(const ImVec2& imgui_pos, ImGuiViewport* viewport_src, ImGuiViewport* viewport_dst) { if (viewport_src == viewport_dst) return imgui_pos; return (imgui_pos - viewport_src->Pos + viewport_src->PlatformPos - viewport_dst->PlatformPos + viewport_dst->Pos); } static ImGuiViewportP* AddUpdateViewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& platform_pos, const ImVec2& size); static void UpdateViewports(); static void UpdateSelectWindowViewport(ImGuiWindow* window); +static void UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport); static void SetCurrentViewport(ImGuiViewportP* viewport); static void SetWindowViewportTranslateToPreservePlatformPos(ImGuiWindow* window, ImGuiViewportP* old_viewport, ImGuiViewportP* new_viewport); static void TranslateOrEraseViewports(int viewport_idx_min, int viewport_idx_max, float delta_x, int delta_idx, ImGuiViewport* viewport_to_erase); @@ -1946,6 +1947,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) ScrollbarSizes = ImVec2(0.0f, 0.0f); ScrollbarX = ScrollbarY = false; ViewportOwned = false; + ViewportTryMerge = false; Active = WasActive = false; WriteAccessed = false; Collapsed = false; @@ -3297,38 +3299,32 @@ static void ImGui::NavUpdate() #endif } -static void ImGui::NewFrameUpdateMovingWindowDropViewport(ImGuiWindow* window) +static void ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport) { - // On release we either drop window over an existing viewport or create a new one - // (We convert position from one viewport space to another, which is unnecessary at the moment but allows us to have viewport overlapping in term of imgui position) ImGuiContext& g = *GImGui; if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) return; + if (!(host_viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) || window->Viewport == host_viewport) + return; - ImRect mouse_viewport_rect = g.MouseRefViewport->GetRect(); - ImVec2 window_pos_in_mouse_viewport = ConvertPlatformPosToViewportPos(ConvertViewportPosToPlatformPos(window->Pos, window->Viewport), g.MouseRefViewport); - ImRect window_rect_in_mouse_viewport = ImRect(window_pos_in_mouse_viewport, window_pos_in_mouse_viewport + window->Size); - if ((g.MouseRefViewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && mouse_viewport_rect.Contains(window_rect_in_mouse_viewport)) + ImRect host_viewport_rect = host_viewport->GetRect(); + ImVec2 window_pos_in_host_viewport = ConvertViewportPosToViewportPos(window->Pos, window->Viewport, host_viewport); + ImRect window_rect_in_host_viewport = ImRect(window_pos_in_host_viewport, window_pos_in_host_viewport + window->Size); + if (!host_viewport_rect.Contains(window_rect_in_host_viewport)) + return; + + // Move to the existing viewport + ImGuiViewportP* old_viewport = window->Viewport; + SetWindowViewportTranslateToPreservePlatformPos(window, window->Viewport, host_viewport); + + // Move child/hosted windows as well (FIXME-OPT) + if (window->ViewportOwned) { - // Drop on an existing viewport - ImGuiViewportP* old_viewport = window->Viewport; - SetWindowViewportTranslateToPreservePlatformPos(window, window->Viewport, g.MouseRefViewport); - - // Move child/hosted windows as well (FIXME-OPT) - if (window->ViewportOwned) - for (int n = 0; n < g.Windows.Size; n++) - if (g.Windows[n]->Viewport == old_viewport) - SetWindowViewportTranslateToPreservePlatformPos(g.Windows[n], old_viewport, g.MouseRefViewport); + for (int n = 0; n < g.Windows.Size; n++) + if (g.Windows[n]->Viewport == old_viewport) + SetWindowViewportTranslateToPreservePlatformPos(g.Windows[n], old_viewport, host_viewport); window->ViewportOwned = false; } - else - { - // Create/persist new viewport - ImVec2 platform_pos = ConvertViewportPosToPlatformPos(window->Pos, window->Viewport); - ImGuiViewportP* viewport = AddUpdateViewport(window, window->ID, 0, platform_pos, window->Size); - IM_ASSERT(viewport == window->Viewport); - SetWindowViewportTranslateToPreservePlatformPos(window, window->Viewport, viewport); - } } static void ImGui::NewFrameUpdateMovingWindow() @@ -3353,7 +3349,7 @@ static void ImGui::NewFrameUpdateMovingWindow() } else { - NewFrameUpdateMovingWindowDropViewport(moving_window); + UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseRefViewport); // Clear the NoInput flag set by the Viewport system moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; @@ -3523,7 +3519,7 @@ static void ImGui::UpdateViewports() viewport_hovered = g.MouseHoveredLastViewport; if (viewport_hovered != NULL && viewport_hovered != g.MouseRefViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) { - g.IO.MousePos = ConvertPlatformPosToViewportPos(ConvertViewportPosToPlatformPos(g.IO.MousePos, g.MouseRefViewport), viewport_hovered); + g.IO.MousePos = ConvertViewportPosToViewportPos(g.IO.MousePos, g.MouseRefViewport, viewport_hovered); g.MouseRefViewport = viewport_hovered; } } @@ -4575,7 +4571,7 @@ void ImGui::Render() for (int viewport_n = 0; viewport_n < g.Viewports.Size; viewport_n++) { ImGuiViewportP* viewport = g.Viewports[viewport_n]; - ImVec2 pos = (g.MouseRefViewport == viewport) ? main_pos : ConvertPlatformPosToViewportPos(ConvertViewportPosToPlatformPos(main_pos, g.MouseRefViewport), viewport); + ImVec2 pos = (g.MouseRefViewport == viewport) ? main_pos : ConvertViewportPosToViewportPos(main_pos, g.MouseRefViewport, viewport); if (viewport->GetRect().Overlaps(ImRect(pos, pos + ImVec2(2,2)*sc + size * sc))) { ImDrawList* draw_list = GetOverlayDrawList(viewport); @@ -5918,13 +5914,13 @@ static ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window) // Child menus typically request _any_ position within the parent menu item, and then our FindBestWindowPosForPopup() function will move the new menu outside the parent bounds. // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu. IM_ASSERT(g.CurrentWindow == window); - ImGuiWindow* parent_menu = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2]; + ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2]; float horizontal_overlap = g.Style.ItemSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x). ImRect r_avoid; - if (parent_menu->DC.MenuBarAppending) - r_avoid = ImRect(-FLT_MAX, parent_menu->Pos.y + parent_menu->TitleBarHeight(), FLT_MAX, parent_menu->Pos.y + parent_menu->TitleBarHeight() + parent_menu->MenuBarHeight()); + if (parent_window->DC.MenuBarAppending) + r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight()); else - r_avoid = ImRect(parent_menu->Pos.x + horizontal_overlap, -FLT_MAX, parent_menu->Pos.x + parent_menu->Size.x - horizontal_overlap - parent_menu->ScrollbarSizes.x, FLT_MAX); + r_avoid = ImRect(parent_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX); return FindBestWindowPosForPopupEx(window->PosFloat, window->Size, &window->AutoPosLastDirection, r_screen, r_avoid); } @@ -6138,7 +6134,7 @@ static void ImGui::SetWindowViewportTranslateToPreservePlatformPos(ImGuiWindow* { if (prev_viewport == curr_viewport) return; - ImVec2 new_pos = ConvertPlatformPosToViewportPos(ConvertViewportPosToPlatformPos(window->PosFloat, prev_viewport), curr_viewport); + ImVec2 new_pos = ConvertViewportPosToViewportPos(window->PosFloat, prev_viewport, curr_viewport); if ((window->FlagsPreviousFrame ^ window->Flags) & ImGuiWindowFlags_NoTitleBar) { // As a convenience, automatically adjust for client rect difference for the common use case of toggling the imgui title-bar when we move our tools to a separate OS window @@ -6158,7 +6154,6 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) { ImGuiContext& g = *GImGui; ImGuiWindowFlags flags = window->Flags; - window->ViewportOwned = false; // Restore main viewport if multi-viewport is not supported by the back-end ImGuiViewportP* main_viewport = g.Viewports[0]; @@ -6166,9 +6161,17 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) { window->Viewport = main_viewport; window->ViewportId = main_viewport->ID; + window->ViewportOwned = false; return; } + if (window->ViewportOwned && window->ViewportTryMerge && g.ActiveId == 0) + { + UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]); + window->ViewportTryMerge = false; + } + window->ViewportOwned = false; + const bool window_is_mouse_tooltip = (flags & ImGuiWindowFlags_Tooltip) && g.NavDisableHighlight && !g.NavDisableMouseHover; const bool window_follow_mouse_viewport = window_is_mouse_tooltip || (g.MovingWindow && g.MovingWindow->RootWindow == window); @@ -6318,7 +6321,7 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au { // Resize from any of the four corners // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position - ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize() * grip.CornerPos; // Corner of the window corresponding to our corner grip + ImVec2 corner_target = ConvertViewportPosToViewportPos(g.IO.MousePos, g.MouseRefViewport, window->Viewport) - g.ActiveIdClickOffset + resize_rect.GetSize() * grip.CornerPos; // Corner of the window corresponding to our corner grip CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPos, &pos_target, &size_target); } if (resize_grip_n == 0 || held || hovered) @@ -6370,9 +6373,11 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au } // Apply back modified position/size to window - if (size_target.x != FLT_MAX) + if (size_target.x != FLT_MAX && (size_target.x != window->SizeFull.x || size_target.y != window->SizeFull.y)) { window->SizeFull = size_target; + if (window->ViewportOwned) + window->ViewportTryMerge = true; MarkIniSettingsDirty(window); } if (pos_target.x != FLT_MAX) @@ -14297,6 +14302,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId); ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); + ImGui::Text("MousePosViewport: 0x%08X, Hovered: 0x%08X -> Ref 0x%08X", g.IO.MousePosViewport, g.IO.MouseHoveredViewport, g.MouseRefViewport->ID); ImGui::TreePop(); } if (show_window_begin_order) diff --git a/imgui_internal.h b/imgui_internal.h index 278648d8..19e92475 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -959,6 +959,7 @@ struct IMGUI_API ImGuiWindow ImVec2 ScrollbarSizes; bool ScrollbarX, ScrollbarY; bool ViewportOwned; + bool ViewportTryMerge; bool Active; // Set to true on Begin(), unless Collapsed bool WasActive; bool WriteAccessed; // Set to true when any widget access the current window