Add framework for Per-Monitor DPI Awareness on Windows
React to the WM_DPICHANGED event and update the size of the child windows and the top-level window. Scale the minimum and maximum window size to the new DPI. Only react to WM_DPICHANGED when DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 is used.
This commit is contained in:
@@ -2830,6 +2830,7 @@ WX_MSW_DECLARE_HANDLE(HBITMAP);
|
|||||||
WX_MSW_DECLARE_HANDLE(HIMAGELIST);
|
WX_MSW_DECLARE_HANDLE(HIMAGELIST);
|
||||||
WX_MSW_DECLARE_HANDLE(HGLOBAL);
|
WX_MSW_DECLARE_HANDLE(HGLOBAL);
|
||||||
WX_MSW_DECLARE_HANDLE(HDC);
|
WX_MSW_DECLARE_HANDLE(HDC);
|
||||||
|
WX_MSW_DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);
|
||||||
typedef WXHINSTANCE WXHMODULE;
|
typedef WXHINSTANCE WXHMODULE;
|
||||||
|
|
||||||
#undef WX_MSW_DECLARE_HANDLE
|
#undef WX_MSW_DECLARE_HANDLE
|
||||||
|
@@ -23,7 +23,11 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef WM_PRINTCLIENT
|
#ifndef WM_PRINTCLIENT
|
||||||
#define WM_PRINTCLIENT 0x318
|
#define WM_PRINTCLIENT 0x0318
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WM_DPICHANGED
|
||||||
|
#define WM_DPICHANGED 0x02E0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef DT_HIDEPREFIX
|
#ifndef DT_HIDEPREFIX
|
||||||
|
@@ -165,6 +165,9 @@ protected:
|
|||||||
int& x, int& y,
|
int& x, int& y,
|
||||||
int& w, int& h) const wxOVERRIDE;
|
int& w, int& h) const wxOVERRIDE;
|
||||||
|
|
||||||
|
// WM_DPICHANGED handler.
|
||||||
|
bool HandleDPIChange(const wxSize& newDPI, const wxRect& newRect);
|
||||||
|
|
||||||
// This field contains the show command to use when showing the window the
|
// This field contains the show command to use when showing the window the
|
||||||
// next time and also indicates whether the window should be considered
|
// next time and also indicates whether the window should be considered
|
||||||
// being iconized or maximized (which may be different from whether it's
|
// being iconized or maximized (which may be different from whether it's
|
||||||
@@ -191,6 +194,14 @@ protected:
|
|||||||
wxWindowRef m_winLastFocused;
|
wxWindowRef m_winLastFocused;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Keep track of the DPI used in this window. So when per-monitor dpi
|
||||||
|
// awareness is enabled, both old and new DPI are known for
|
||||||
|
// wxDPIChangedEvent and wxWindow::MSWUpdateOnDPIChange.
|
||||||
|
wxSize m_activeDPI;
|
||||||
|
|
||||||
|
// This window supports handling per-monitor DPI awareness when the
|
||||||
|
// application manifest contains <dpiAwareness>PerMonitorV2</dpiAwareness>.
|
||||||
|
bool m_perMonitorDPIaware;
|
||||||
|
|
||||||
// The system menu: initially NULL but can be set (once) by
|
// The system menu: initially NULL but can be set (once) by
|
||||||
// MSWGetSystemMenu(). Owned by this window.
|
// MSWGetSystemMenu(). Owned by this window.
|
||||||
|
@@ -586,6 +586,10 @@ public:
|
|||||||
// Should be overridden by all classes storing the "last focused" window.
|
// Should be overridden by all classes storing the "last focused" window.
|
||||||
virtual void WXDoUpdatePendingFocus(wxWindow* WXUNUSED(win)) {}
|
virtual void WXDoUpdatePendingFocus(wxWindow* WXUNUSED(win)) {}
|
||||||
|
|
||||||
|
// Called from WM_DPICHANGED handler for all windows to let them update
|
||||||
|
// any sizes and fonts used internally when the DPI changes.
|
||||||
|
void MSWUpdateOnDPIChange(const wxSize& oldDPI, const wxSize& newDPI);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// this allows you to implement standard control borders without
|
// this allows you to implement standard control borders without
|
||||||
// repeating the code in different classes that are not derived from
|
// repeating the code in different classes that are not derived from
|
||||||
|
@@ -104,6 +104,9 @@ void wxTopLevelWindowMSW::Init()
|
|||||||
m_fsIsShowing = false;
|
m_fsIsShowing = false;
|
||||||
|
|
||||||
m_menuSystem = NULL;
|
m_menuSystem = NULL;
|
||||||
|
|
||||||
|
m_activeDPI = wxDefaultSize;
|
||||||
|
m_perMonitorDPIaware = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
WXDWORD wxTopLevelWindowMSW::MSWGetStyle(long style, WXDWORD *exflags) const
|
WXDWORD wxTopLevelWindowMSW::MSWGetStyle(long style, WXDWORD *exflags) const
|
||||||
@@ -246,6 +249,26 @@ WXHWND wxTopLevelWindowMSW::MSWGetParent() const
|
|||||||
return (WXHWND)hwndParent;
|
return (WXHWND)hwndParent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool wxTopLevelWindowMSW::HandleDPIChange(const wxSize& newDPI, const wxRect& newRect)
|
||||||
|
{
|
||||||
|
if ( !m_perMonitorDPIaware )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( newDPI != m_activeDPI )
|
||||||
|
{
|
||||||
|
MSWUpdateOnDPIChange(m_activeDPI, newDPI);
|
||||||
|
m_activeDPI = newDPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetSize(newRect);
|
||||||
|
|
||||||
|
Refresh();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
WXLRESULT wxTopLevelWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam)
|
WXLRESULT wxTopLevelWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam)
|
||||||
{
|
{
|
||||||
WXLRESULT rc = 0;
|
WXLRESULT rc = 0;
|
||||||
@@ -306,6 +329,17 @@ WXLRESULT wxTopLevelWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WX
|
|||||||
#endif // #ifndef __WXUNIVERSAL__
|
#endif // #ifndef __WXUNIVERSAL__
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case WM_DPICHANGED:
|
||||||
|
{
|
||||||
|
const RECT* const prcNewWindow =
|
||||||
|
reinterpret_cast<const RECT*>(lParam);
|
||||||
|
|
||||||
|
processed = HandleDPIChange(wxSize(LOWORD(wParam),
|
||||||
|
HIWORD(wParam)),
|
||||||
|
wxRectFromRECT(*prcNewWindow));
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !processed )
|
if ( !processed )
|
||||||
@@ -314,6 +348,47 @@ WXLRESULT wxTopLevelWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WX
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
static bool IsPerMonitorDPIAware(HWND hwnd)
|
||||||
|
{
|
||||||
|
bool dpiAware = false;
|
||||||
|
|
||||||
|
// Determine if 'Per Monitor v2' DPI awareness is enabled in the
|
||||||
|
// applications manifest.
|
||||||
|
#if wxUSE_DYNLIB_CLASS
|
||||||
|
#define WXDPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((WXDPI_AWARENESS_CONTEXT)-4)
|
||||||
|
typedef WXDPI_AWARENESS_CONTEXT(WINAPI * GetWindowDpiAwarenessContext_t)(HWND hwnd);
|
||||||
|
typedef BOOL(WINAPI * AreDpiAwarenessContextsEqual_t)(WXDPI_AWARENESS_CONTEXT dpiContextA, WXDPI_AWARENESS_CONTEXT dpiContextB);
|
||||||
|
static GetWindowDpiAwarenessContext_t s_pfnGetWindowDpiAwarenessContext = NULL;
|
||||||
|
static AreDpiAwarenessContextsEqual_t s_pfnAreDpiAwarenessContextsEqual = NULL;
|
||||||
|
static bool s_initDone = false;
|
||||||
|
|
||||||
|
if ( !s_initDone )
|
||||||
|
{
|
||||||
|
wxLoadedDLL dllUser32("user32.dll");
|
||||||
|
wxDL_INIT_FUNC(s_pfn, GetWindowDpiAwarenessContext, dllUser32);
|
||||||
|
wxDL_INIT_FUNC(s_pfn, AreDpiAwarenessContextsEqual, dllUser32);
|
||||||
|
s_initDone = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( s_pfnGetWindowDpiAwarenessContext && s_pfnAreDpiAwarenessContextsEqual )
|
||||||
|
{
|
||||||
|
WXDPI_AWARENESS_CONTEXT dpiAwarenessContext = s_pfnGetWindowDpiAwarenessContext(hwnd);
|
||||||
|
|
||||||
|
if ( s_pfnAreDpiAwarenessContextsEqual(dpiAwarenessContext, WXDPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) == TRUE )
|
||||||
|
{
|
||||||
|
dpiAware = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // wxUSE_DYNLIB_CLASS
|
||||||
|
|
||||||
|
return dpiAware;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
bool wxTopLevelWindowMSW::CreateDialog(const void *dlgTemplate,
|
bool wxTopLevelWindowMSW::CreateDialog(const void *dlgTemplate,
|
||||||
const wxString& title,
|
const wxString& title,
|
||||||
const wxPoint& pos,
|
const wxPoint& pos,
|
||||||
@@ -484,6 +559,10 @@ bool wxTopLevelWindowMSW::Create(wxWindow *parent,
|
|||||||
EnableCloseButton(false);
|
EnableCloseButton(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_activeDPI = GetDPI();
|
||||||
|
|
||||||
|
m_perMonitorDPIaware = IsPerMonitorDPIAware(GetHwnd());
|
||||||
|
|
||||||
// for standard dialogs the dialog manager generates WM_CHANGEUISTATE
|
// for standard dialogs the dialog manager generates WM_CHANGEUISTATE
|
||||||
// itself but for custom windows we have to do it ourselves in order to
|
// itself but for custom windows we have to do it ourselves in order to
|
||||||
// make the keyboard indicators (such as underlines for accelerators and
|
// make the keyboard indicators (such as underlines for accelerators and
|
||||||
|
@@ -4837,6 +4837,47 @@ wxSize wxWindowMSW::GetDPI() const
|
|||||||
return dpi;
|
return dpi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to update the given coordinate by the scaling factor if it
|
||||||
|
// is set, i.e. different from wxDefaultCoord.
|
||||||
|
static void ScaleCoordIfSet(int& coord, float scaleFactor)
|
||||||
|
{
|
||||||
|
if ( coord != wxDefaultCoord )
|
||||||
|
{
|
||||||
|
const float coordScaled = coord * scaleFactor;
|
||||||
|
coord = scaleFactor > 1.0 ? ceil(coordScaled) : floor(coordScaled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
wxWindowMSW::MSWUpdateOnDPIChange(const wxSize& oldDPI, const wxSize& newDPI)
|
||||||
|
{
|
||||||
|
// update min and max size if necessary
|
||||||
|
const float scaleFactor = (float)newDPI.y / oldDPI.y;
|
||||||
|
|
||||||
|
ScaleCoordIfSet(m_minHeight, scaleFactor);
|
||||||
|
ScaleCoordIfSet(m_minWidth, scaleFactor);
|
||||||
|
ScaleCoordIfSet(m_maxHeight, scaleFactor);
|
||||||
|
ScaleCoordIfSet(m_maxWidth, scaleFactor);
|
||||||
|
|
||||||
|
InvalidateBestSize();
|
||||||
|
|
||||||
|
// update children
|
||||||
|
wxWindowList::compatibility_iterator current = GetChildren().GetFirst();
|
||||||
|
while ( current )
|
||||||
|
{
|
||||||
|
wxWindow *childWin = current->GetData();
|
||||||
|
// Update all children, except other top-level windows.
|
||||||
|
// These could be on a different monitor and will get their own
|
||||||
|
// dpi-changed event.
|
||||||
|
if ( childWin && !childWin->IsTopLevel() )
|
||||||
|
{
|
||||||
|
childWin->MSWUpdateOnDPIChange(oldDPI, newDPI);
|
||||||
|
}
|
||||||
|
|
||||||
|
current = current->GetNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// colours and palettes
|
// colours and palettes
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
Reference in New Issue
Block a user