Viewport: Clamp windows within monitors + fallback rescue window when it is out of sight (e.g. removed monitor, changed resolution) + Win32: declare primary monitor at the beginning of the list. (#1542)

This commit is contained in:
omar 2018-04-23 23:01:37 +02:00
parent 010757266e
commit 376f2aec54
4 changed files with 60 additions and 13 deletions

View File

@ -259,7 +259,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i
- focus: unable to use SetKeyboardFocusHere() on clipped widgets. (#787) - focus: unable to use SetKeyboardFocusHere() on clipped widgets. (#787)
- viewport: popup/tooltip glitches when appearing near the monitor limits (e.g. opening a menu). - viewport: popup/tooltip glitches when appearing near the monitor limits (e.g. opening a menu).
- viewport: clamp windows position within monitors (especially important on monitor configuration change) -> clamp to closest rectangle.
- viewport: platform: introduce getfocus/setfocus api, so e.g. focus flags can be honored, imgui-side ctrl-tab can focus os window, OS alt-tab can focus imgui window etc. - viewport: platform: introduce getfocus/setfocus api, so e.g. focus flags can be honored, imgui-side ctrl-tab can focus os window, OS alt-tab can focus imgui window etc.
- viewport: store per-viewport/monitor DPI in .ini file so an application reload or main window changing DPI on reload can be properly patched for. - viewport: store per-viewport/monitor DPI in .ini file so an application reload or main window changing DPI on reload can be properly patched for.
- viewport: IME positioning are wrong. - viewport: IME positioning are wrong.

View File

@ -578,7 +578,11 @@ static BOOL CALLBACK ImGui_ImplWin32_UpdateMonitors_EnumFunc(HMONITOR monitor, H
imgui_monitor.WorkMin = ImVec2((float)info.rcWork.left, (float)info.rcWork.top); imgui_monitor.WorkMin = ImVec2((float)info.rcWork.left, (float)info.rcWork.top);
imgui_monitor.WorkMax = ImVec2((float)info.rcWork.right, (float)info.rcWork.bottom); imgui_monitor.WorkMax = ImVec2((float)info.rcWork.right, (float)info.rcWork.bottom);
imgui_monitor.DpiScale = ImGui_ImplWin32_GetDpiScaleForMonitor(monitor); imgui_monitor.DpiScale = ImGui_ImplWin32_GetDpiScaleForMonitor(monitor);
ImGui::GetPlatformIO().Monitors.push_back(imgui_monitor); ImGuiPlatformIO& io = ImGui::GetPlatformIO();
if (info.dwFlags & MONITORINFOF_PRIMARY)
io.Monitors.push_front(imgui_monitor);
else
io.Monitors.push_back(imgui_monitor);
return TRUE; return TRUE;
} }

View File

@ -832,7 +832,7 @@ ImGuiStyle::ImGuiStyle()
GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar
GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
DisplayWindowPadding = ImVec2(22,22); // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows. DisplayWindowPadding = ImVec2(20,20); // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows.
DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows.
MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU. AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
@ -3817,6 +3817,8 @@ void ImGui::NewFrame()
// Disable feature, our back-ends do not support it // Disable feature, our back-ends do not support it
g.IO.ConfigFlags &= ~ImGuiConfigFlags_ViewportsEnable; g.IO.ConfigFlags &= ~ImGuiConfigFlags_ViewportsEnable;
} }
// Perform simple checks on platform monitor data + compute a total bounding box for quick early outs
for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++) for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++)
{ {
ImGuiPlatformMonitor& mon = g.PlatformIO.Monitors[monitor_n]; ImGuiPlatformMonitor& mon = g.PlatformIO.Monitors[monitor_n];
@ -6122,13 +6124,37 @@ static int FindPlatformMonitorForPos(ImVec2 platform_pos)
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++) for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++)
{ {
const ImGuiPlatformMonitor* monitor = &g.PlatformIO.Monitors[monitor_n]; const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n];
if (ImRect(monitor->FullMin, monitor->FullMax).Contains(platform_pos)) if (ImRect(monitor.FullMin, monitor.FullMax).Contains(platform_pos))
return monitor_n; return monitor_n;
} }
return -1; return -1;
} }
// Search for the monitor with the largest intersection area with the given rectangle
// We generally try to avoid searching loops but the monitor count should be very small here
static int FindPlatformMonitorForRect(const ImRect& rect)
{
ImGuiContext& g = *GImGui;
float surface_threshold = rect.GetWidth() * rect.GetHeight() * 0.5f;
int best_monitor_n = -1;
float best_monitor_surface = 0.001f;
for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size && best_monitor_surface < surface_threshold; monitor_n++)
{
const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n];
if (ImRect(monitor.FullMin, monitor.FullMax).Contains(rect))
return monitor_n;
ImRect overlapping_rect = rect;
overlapping_rect.ClipWithFull(ImRect(monitor.FullMin, monitor.FullMax));
float overlapping_surface = overlapping_rect.GetWidth() * overlapping_rect.GetHeight();
if (overlapping_surface < best_monitor_surface)
continue;
best_monitor_surface = overlapping_surface;
best_monitor_n = monitor_n;
}
return best_monitor_n;
}
// FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten. // FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten.
static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window)
{ {
@ -6367,6 +6393,11 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au
window->Size = window->SizeFull; window->Size = window->SizeFull;
} }
static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& rect, const ImVec2& padding)
{
window->PosFloat = ImMin(rect.Max - padding, ImMax(window->PosFloat + window->Size, rect.Min + padding) - window->Size);
}
// Push a new ImGui window to add widgets to. // Push a new ImGui window to add widgets to.
// - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair. // - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair.
// - Begin/End can be called multiple times during the frame with the same window name to append content. // - Begin/End can be called multiple times during the frame with the same window name to append content.
@ -6680,17 +6711,29 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->Viewport->Flags |= ImGuiViewportFlags_NoRendererClear; window->Viewport->Flags |= ImGuiViewportFlags_NoRendererClear;
} }
// Clamp position so window stays visible within its viewport // Clamp position 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. // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
ImRect viewport_rect = window->Viewport->GetRect(); ImRect viewport_rect = window->Viewport->GetRect();
if (!window->ViewportOwned && !window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
if (viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f)
{ {
ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); ImVec2 clamp_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
window->PosFloat = ImMax(window->PosFloat + window->Size, viewport_rect.Min + padding) - window->Size; if (!window->ViewportOwned && viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f)
window->PosFloat = ImMin(window->PosFloat, viewport_rect.Max - padding); ClampWindowRect(window, viewport_rect, clamp_padding);
else if (window->ViewportOwned && g.PlatformIO.Monitors.Size > 0)
{
int monitor_n = FindPlatformMonitorForRect(viewport_rect);
if (monitor_n == -1)
{
// Fallback for "lost" window (e.g. a monitor disconnected): we move the window back over the main viewport
window->PosFloat = g.Viewports[0]->Pos + style.DisplayWindowPadding;
window->ViewportTryMerge = true;
}
else
{
ClampWindowRect(window, ImRect(g.PlatformIO.Monitors[monitor_n].WorkMin, g.PlatformIO.Monitors[monitor_n].WorkMax), clamp_padding);
}
}
} }
window->Pos = ImFloor(window->PosFloat); window->Pos = ImFloor(window->PosFloat);
// Lock window rounding for the frame (so that altering them doesn't cause inconsistencies) // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
@ -14208,6 +14251,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
else else
ImGui::BulletText("NavRectRel[0]: <None>"); ImGui::BulletText("NavRectRel[0]: <None>");
ImGui::BulletText("Viewport: %d, ViewportId: 0x%08X, ViewportPlatformPos: (%.1f,%.1f)", window->Viewport ? window->Viewport->Idx : -1, window->ViewportId, window->ViewportPos.x, window->ViewportPos.y); ImGui::BulletText("Viewport: %d, ViewportId: 0x%08X, ViewportPlatformPos: (%.1f,%.1f)", window->Viewport ? window->Viewport->Idx : -1, window->ViewportId, window->ViewportPos.x, window->ViewportPos.y);
ImGui::BulletText("Monitor: %d", FindPlatformMonitorForRect(window->Rect()));
if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size)) if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))

View File

@ -1004,7 +1004,7 @@ struct ImGuiStyle
float GrabMinSize; // Minimum width/height of a grab box for slider/scrollbar. float GrabMinSize; // Minimum width/height of a grab box for slider/scrollbar.
float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f,0.5f) for horizontally+vertically centered. ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f,0.5f) for horizontally+vertically centered.
ImVec2 DisplayWindowPadding; // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows. ImVec2 DisplayWindowPadding; // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows.
ImVec2 DisplaySafeAreaPadding; // If you cannot see the edges of your screen (e.g. on a TV) increase the safe area padding. Apply to popups/tooltips as well regular windows. NB: Prefer configuring your TV sets correctly! ImVec2 DisplaySafeAreaPadding; // If you cannot see the edges of your screen (e.g. on a TV) increase the safe area padding. Apply to popups/tooltips as well regular windows. NB: Prefer configuring your TV sets correctly!
float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
bool AntiAliasedLines; // Enable anti-aliasing on lines/borders. Disable if you are really tight on CPU/GPU. bool AntiAliasedLines; // Enable anti-aliasing on lines/borders. Disable if you are really tight on CPU/GPU.