From 5e63711084c4596ff0c6cdc73d041094250d95a4 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 9 Mar 2018 19:08:47 +0100 Subject: [PATCH] Viewport, DPI: Some early work on per-viewport DPI support. At the moment the easiest way is to replace fonts during the ChangedViewport callback, but down the line we should aim at handling some of it at ImFont level. (#1542, #1676) --- examples/directx11_example/main.cpp | 15 ++++++++++++ examples/imgui_impl_win32.cpp | 13 ++++++++++ imgui.cpp | 37 ++++++++++++++++++++--------- imgui.h | 18 +++++++------- imgui_internal.h | 9 +++---- 5 files changed, 69 insertions(+), 23 deletions(-) diff --git a/examples/directx11_example/main.cpp b/examples/directx11_example/main.cpp index 3cec017d..6e81b811 100644 --- a/examples/directx11_example/main.cpp +++ b/examples/directx11_example/main.cpp @@ -67,6 +67,10 @@ void CleanupDeviceD3D() if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; } } +#ifndef WM_DPICHANGED +#define WM_DPICHANGED 0x02E0 // From Windows SDK 8.1+ headers +#endif + extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { @@ -92,6 +96,15 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) case WM_DESTROY: PostQuitMessage(0); return 0; + case WM_DPICHANGED: + if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_EnableDpiScaleViewports) + { + //const int dpi = HIWORD(wParam); + //printf("WM_DPICHANGED to %d (%.0f%%)\n", dpi, (float)dpi / 96.0f * 100.0f); + const RECT* suggested_rect = (RECT*)lParam; + ::SetWindowPos(hWnd, NULL, suggested_rect->left, suggested_rect->top, suggested_rect->right - suggested_rect->left, suggested_rect->bottom - suggested_rect->top, SWP_NOZORDER | SWP_NOACTIVATE); + } + break; } return DefWindowProc(hWnd, msg, wParam, lParam); } @@ -121,6 +134,8 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; + io.ConfigFlags |= ImGuiConfigFlags_EnableDpiScaleFonts; + io.ConfigFlags |= ImGuiConfigFlags_EnableDpiScaleViewports; io.ConfigFlags |= ImGuiConfigFlags_PlatformNoTaskBar; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index eeea68a8..ef0d6449 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -470,6 +470,18 @@ static void ImGui_ImplWin32_SetWindowTitle(ImGuiViewport* viewport, const char* ::SetWindowTextA(data->Hwnd, title); } +static float ImGui_ImplWin32_GetWindowDpiScale(ImGuiViewport* viewport) +{ + ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData; + if (data && data->Hwnd) + return ImGui_ImplWin32_GetDpiScaleForHwnd(data->Hwnd); + + // The first frame a viewport is created we don't have a window yet + return ImGui_ImplWin32_GetDpiScaleForRect( + (int)(viewport->PlatformOsDesktopPos.x), (int)(viewport->PlatformOsDesktopPos.y), + (int)(viewport->PlatformOsDesktopPos.x + viewport->Size.x), (int)(viewport->PlatformOsDesktopPos.y + viewport->Size.y)); +} + static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam)) @@ -531,6 +543,7 @@ static void ImGui_ImplWin32_InitPlatformInterface() io.PlatformInterface.SetWindowSize = ImGui_ImplWin32_SetWindowSize; io.PlatformInterface.GetWindowSize = ImGui_ImplWin32_GetWindowSize; io.PlatformInterface.SetWindowTitle = ImGui_ImplWin32_SetWindowTitle; + io.PlatformInterface.GetWindowDpiScale = ImGui_ImplWin32_GetWindowDpiScale; // Register main window handle ImGuiViewport* main_viewport = ImGui::GetMainViewport(); diff --git a/imgui.cpp b/imgui.cpp index fefcfda9..eaf8df1d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1944,7 +1944,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) LastFrameActive = -1; ItemWidthDefault = 0.0f; - FontWindowScale = 1.0f; + FontWindowScale = FontDpiScale = 1.0f; DrawList = IM_NEW(ImDrawList)(&context->DrawListSharedData); DrawList->_OwnerName = Name; @@ -3388,13 +3388,20 @@ static void ImGui::UpdateViewports() if (g.IO.PlatformInterface.GetWindowDpiScale) new_dpi_scale = g.IO.PlatformInterface.GetWindowDpiScale(viewport); else - new_dpi_scale = (viewport->PlatformDpiScale != 0.0f) ? viewport->PlatformDpiScale : 1.0f; - if (viewport->PlatformDpiScale != 0.0f && new_dpi_scale != viewport->PlatformDpiScale) + new_dpi_scale = (viewport->DpiScale != 0.0f) ? viewport->DpiScale : 1.0f; + if (viewport->DpiScale != 0.0f && new_dpi_scale != viewport->DpiScale) { - float scale_factor = new_dpi_scale / viewport->PlatformDpiScale; - ScaleWindowsInViewport(viewport, scale_factor); + float scale_factor = new_dpi_scale / viewport->DpiScale; + if (g.IO.ConfigFlags & ImGuiConfigFlags_EnableDpiScaleViewports) + ScaleWindowsInViewport(viewport, scale_factor); + //if (viewport == GetMainViewport()) + // g.IO.PlatformInterface.SetWindowSize(viewport, viewport->Size * scale_factor); + + // FIXME-DPI: We need to preserve our pivots + //if (g.MovingWindow) + // g.ActiveIdClickOffset = g.ActiveIdClickOffset * scale_factor; } - viewport->PlatformDpiScale = new_dpi_scale; + viewport->DpiScale = new_dpi_scale; } // Update main viewport with current size (and OS window position, if known) @@ -4527,11 +4534,9 @@ void ImGui::SetCurrentViewport(ImGuiViewport* viewport) viewport->LastFrameActive = g.FrameCount; if (g.CurrentViewport == viewport) return; - if (g.CurrentViewport && g.IO.PlatformInterface.EndViewport) - g.IO.PlatformInterface.EndViewport(g.CurrentViewport); g.CurrentViewport = viewport; - if (g.CurrentViewport && g.IO.PlatformInterface.BeginViewport) - g.IO.PlatformInterface.BeginViewport(g.CurrentViewport); + if (g.CurrentViewport && g.IO.PlatformInterface.ChangedViewport) + g.IO.PlatformInterface.ChangedViewport(g.CurrentViewport); } ImGuiViewport* ImGui::Viewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& os_desktop_pos, const ImVec2& size) @@ -4558,6 +4563,10 @@ ImGuiViewport* ImGui::Viewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFla viewport->Flags = flags; viewport->PlatformOsDesktopPos = os_desktop_pos; viewport->LastFrameActive = g.FrameCount; + + // Request an initial DpiScale before the OS platform window creation + if (g.IO.PlatformInterface.GetWindowDpiScale) + viewport->DpiScale = g.IO.PlatformInterface.GetWindowDpiScale(viewport); return viewport; } @@ -6322,6 +6331,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) UpdateWindowViewport(window, window_pos_set_by_api); SetCurrentViewport(window->Viewport); + window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_EnableDpiScaleFonts) ? window->Viewport->DpiScale : 1.0f; SetCurrentWindow(window); flags = window->Flags; @@ -13805,6 +13815,11 @@ static void ScaleWindow(ImGuiWindow* window, float scale) void ImGui::ScaleWindowsInViewport(ImGuiViewport* viewport, float scale) { ImGuiContext& g = *GImGui; + if (g.IO.MousePosViewport == viewport->ID) + { + //g.IO.MousePos = g.IO.MousePosPrev = ImFloor((g.IO.MousePos - viewport->Pos) * scale) + viewport->Pos; + //g.IO.MouseDelta = ImVec2(0,0); + } if (viewport->Window) { ScaleWindow(viewport->Window, scale); @@ -13992,7 +14007,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) { ImGui::BulletText("Pos: (%.0f,%.0f)", viewport->Pos.x, viewport->Pos.y); ImGui::BulletText("Flags: 0x%04X", viewport->Flags); - ImGui::BulletText("PlatformOsDesktopPos: (%.0f,%.0f); DpiScale: %.0f%%", viewport->PlatformOsDesktopPos.x, viewport->PlatformOsDesktopPos.y, viewport->PlatformDpiScale * 100.0f); + ImGui::BulletText("PlatformOsDesktopPos: (%.0f,%.0f); DpiScale: %.0f%%", viewport->PlatformOsDesktopPos.x, viewport->PlatformOsDesktopPos.y, viewport->DpiScale * 100.0f); for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[0].Size; draw_list_i++) Funcs::NodeDrawList(NULL, viewport->DrawDataBuilder.Layers[0][draw_list_i], "DrawList"); ImGui::TreePop(); diff --git a/imgui.h b/imgui.h index 1f32bdc1..04caa0cd 100644 --- a/imgui.h +++ b/imgui.h @@ -779,12 +779,15 @@ enum ImGuiConfigFlags_ // [BETA] Viewports ImGuiConfigFlags_EnableViewports = 1 << 4, // Viewport enable flags (require both ImGuiConfigFlags_PlatformHasViewports + ImGuiConfigFlags_RendererHasViewports set by the respective back-ends) - ImGuiConfigFlags_PlatformNoTaskBar = 1 << 5, - ImGuiConfigFlags_PlatformHasViewports = 1 << 6, // Back-end Platform supports multiple viewports - ImGuiConfigFlags_PlatformHasMouseHoveredViewport = 1 << 7, // Back-end Platform supports setting io.MouseHoveredViewport to the viewport directly under the mouse _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag and _REGARDLESS_ of whether another viewport is focused and may be capturing the mouse. This information is _NOT EASY_ to provide correctly with most high-level engines. Don't see this without studying how the examples/ back-end handle it. - ImGuiConfigFlags_PlatformHasWantMoveMouseSupport = 1 << 8, // Back-end Platform supports io.WantMoveMouse request by updating the OS mouse cursor position (currently only used by ImGuiConfigFlags_NavMoveMouse feature, will be useful for widgets teleporting/wrapping the cursor) - ImGuiConfigFlags_PlatformHasWindowAlpha = 1 << 9, // Back-end Platform supports transparent windows - ImGuiConfigFlags_RendererHasViewports = 1 << 10, // Back-end Renderer supports multiple viewports + ImGuiConfigFlags_EnableDpiScaleViewports = 1 << 5, + ImGuiConfigFlags_EnableDpiScaleFonts = 1 << 6, + + ImGuiConfigFlags_PlatformNoTaskBar = 1 << 10, + ImGuiConfigFlags_PlatformHasViewports = 1 << 11, // Back-end Platform supports multiple viewports + ImGuiConfigFlags_PlatformHasMouseHoveredViewport = 1 << 12, // Back-end Platform supports setting io.MouseHoveredViewport to the viewport directly under the mouse _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag and _REGARDLESS_ of whether another viewport is focused and may be capturing the mouse. This information is _NOT EASY_ to provide correctly with most high-level engines. Don't see this without studying how the examples/ back-end handle it. + ImGuiConfigFlags_PlatformHasWantMoveMouseSupport = 1 << 13, // Back-end Platform supports io.WantMoveMouse request by updating the OS mouse cursor position (currently only used by ImGuiConfigFlags_NavMoveMouse feature, will be useful for widgets teleporting/wrapping the cursor) + ImGuiConfigFlags_PlatformHasWindowAlpha = 1 << 14, // Back-end Platform supports transparent windows + ImGuiConfigFlags_RendererHasViewports = 1 << 15, // Back-end Renderer supports multiple viewports // Platform Info (free of use, for user/application convenience) ImGuiConfigFlags_IsSRGB = 1 << 20, // Back-end is SRGB-aware (Storage flag to allow your back-end to communicate to shared widgets. Not used by core ImGui) @@ -965,8 +968,7 @@ struct ImGuiPlatformInterface // FIXME-DPI float (*GetWindowDpiScale)(ImGuiViewport* viewport); // (Optional) - void (*BeginViewport)(ImGuiViewport* viewport); // (Optional) Called during Begin() every time the viewport we are outputting into changes (viewport = next viewport) - void (*EndViewport)(ImGuiViewport* viewport); // (Optional) Called during Begin() every time the viewport we are outputting into changes (viewport = previous viewport) + void (*ChangedViewport)(ImGuiViewport* viewport); // (Optional) Called during Begin() every time the viewport we are outputting into changes (viewport = next viewport) }; // (Optional) Setup required only if (io.ConfigFlags & ImGuiConfigFlags_EnableMultiViewport) is enabled diff --git a/imgui_internal.h b/imgui_internal.h index 4aa9a3b9..56a8a1bd 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -534,19 +534,19 @@ struct ImGuiViewport ImGuiWindow* Window; ImVec2 Pos; // Position in imgui virtual space (Pos.y == 0.0) ImVec2 Size; + float DpiScale; ImDrawData DrawData; ImDrawDataBuilder DrawDataBuilder; // [Optional] OS/Platform Layer data. This is to allow the creation/manipulate of multiple OS/Platform windows. Not all back-ends will allow this. ImVec2 PlatformOsDesktopPos; // Position in OS desktop/native space - float PlatformDpiScale; // FIXME-DPI: Unused void* PlatformUserData; // void* to hold custom data structure for the platform (e.g. windowing info, render context) void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. HWND, GlfwWindow*) bool PlatformRequestClose; // Platform window requested closure bool PlatformRequestResize; // Platform window requested resize void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. framebuffer) - ImGuiViewport(ImGuiID id, int idx) { ID = id; Idx = idx; Flags = 0; LastFrameActive = LastFrameAsRefViewport = -1; LastNameHash = 0; Window = NULL; PlatformDpiScale = 0.0f; PlatformUserData = PlatformHandle = NULL; PlatformRequestClose = PlatformRequestResize = false; RendererUserData = NULL; } + ImGuiViewport(ImGuiID id, int idx) { ID = id; Idx = idx; Flags = 0; LastFrameActive = LastFrameAsRefViewport = -1; LastNameHash = 0; Window = NULL; DpiScale = 0.0f; PlatformUserData = PlatformHandle = NULL; PlatformRequestClose = PlatformRequestResize = false; RendererUserData = NULL; } ~ImGuiViewport() { IM_ASSERT(PlatformUserData == NULL && RendererUserData == NULL); } ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } float GetNextX() const { const float SPACING = 4.0f; return Pos.x + Size.x + SPACING; } @@ -1010,7 +1010,8 @@ struct IMGUI_API ImGuiWindow ImGuiMenuColumns MenuColumns; // Simplified columns storage for menu items ImGuiStorage StateStorage; ImVector ColumnsStorage; - float FontWindowScale; // Scale multiplier per-window + float FontWindowScale; // User scale multiplier per-window + float FontDpiScale; ImDrawList* DrawList; ImGuiWindow* ParentWindow; // If we are a child _or_ popup window, this is pointing to our parent. Otherwise NULL. ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. @@ -1042,7 +1043,7 @@ public: // We don't use g.FontSize because the window may be != g.CurrentWidow. ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x+Size.x, Pos.y+Size.y); } - float CalcFontSize() const { return GImGui->FontBaseSize * FontWindowScale; } + float CalcFontSize() const { return GImGui->FontBaseSize * FontWindowScale * FontDpiScale; } float TitleBarHeight() const { return (Flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f; } ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); } float MenuBarHeight() const { return (Flags & ImGuiWindowFlags_MenuBar) ? CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f : 0.0f; }