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; }