diff --git a/include/wx/defs.h b/include/wx/defs.h index 1871ae13e4..f349de2cb2 100644 --- a/include/wx/defs.h +++ b/include/wx/defs.h @@ -2830,6 +2830,7 @@ WX_MSW_DECLARE_HANDLE(HBITMAP); WX_MSW_DECLARE_HANDLE(HIMAGELIST); WX_MSW_DECLARE_HANDLE(HGLOBAL); WX_MSW_DECLARE_HANDLE(HDC); +WX_MSW_DECLARE_HANDLE(DPI_AWARENESS_CONTEXT); typedef WXHINSTANCE WXHMODULE; #undef WX_MSW_DECLARE_HANDLE diff --git a/include/wx/event.h b/include/wx/event.h index c57e6ff0ef..3b5a975c01 100644 --- a/include/wx/event.h +++ b/include/wx/event.h @@ -641,6 +641,7 @@ class WXDLLIMPEXP_FWD_CORE wxMenuEvent; class WXDLLIMPEXP_FWD_CORE wxContextMenuEvent; class WXDLLIMPEXP_FWD_CORE wxSysColourChangedEvent; class WXDLLIMPEXP_FWD_CORE wxDisplayChangedEvent; +class WXDLLIMPEXP_FWD_CORE wxDPIChangedEvent; class WXDLLIMPEXP_FWD_CORE wxQueryNewPaletteEvent; class WXDLLIMPEXP_FWD_CORE wxPaletteChangedEvent; class WXDLLIMPEXP_FWD_CORE wxJoystickEvent; @@ -793,6 +794,7 @@ wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_MENU_HIGHLIGHT, wxMenuEvent); wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_CONTEXT_MENU, wxContextMenuEvent); wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_SYS_COLOUR_CHANGED, wxSysColourChangedEvent); wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_DISPLAY_CHANGED, wxDisplayChangedEvent); +wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_DPI_CHANGED, wxDPIChangedEvent); wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_QUERY_NEW_PALETTE, wxQueryNewPaletteEvent); wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_PALETTE_CHANGED, wxPaletteChangedEvent); wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_JOY_BUTTON_DOWN, wxJoystickEvent); @@ -3031,6 +3033,32 @@ public: virtual wxEvent *Clone() const wxOVERRIDE { return new wxDisplayChangedEvent(*this); } }; +/* + wxEVT_DPI_CHANGED + */ +class WXDLLIMPEXP_CORE wxDPIChangedEvent : public wxEvent +{ +public: + explicit + wxDPIChangedEvent(const wxSize& oldDPI = wxDefaultSize, + const wxSize& newDPI = wxDefaultSize) + : wxEvent(0, wxEVT_DPI_CHANGED), + m_oldDPI(oldDPI), + m_newDPI(newDPI) + { } + + wxSize GetOldDPI() const { return m_oldDPI; } + wxSize GetNewDPI() const { return m_newDPI; } + + virtual wxEvent *Clone() const wxOVERRIDE { return new wxDPIChangedEvent(*this); } + +private: + wxSize m_oldDPI; + wxSize m_newDPI; + + wxDECLARE_DYNAMIC_CLASS_NO_ASSIGN(wxDPIChangedEvent); +}; + /* wxEVT_PALETTE_CHANGED */ @@ -4108,6 +4136,7 @@ typedef void (wxEvtHandler::*wxDropFilesEventFunction)(wxDropFilesEvent&); typedef void (wxEvtHandler::*wxInitDialogEventFunction)(wxInitDialogEvent&); typedef void (wxEvtHandler::*wxSysColourChangedEventFunction)(wxSysColourChangedEvent&); typedef void (wxEvtHandler::*wxDisplayChangedEventFunction)(wxDisplayChangedEvent&); +typedef void (wxEvtHandler::*wxDPIChangedEventFunction)(wxDPIChangedEvent&); typedef void (wxEvtHandler::*wxUpdateUIEventFunction)(wxUpdateUIEvent&); typedef void (wxEvtHandler::*wxCloseEventFunction)(wxCloseEvent&); typedef void (wxEvtHandler::*wxShowEventFunction)(wxShowEvent&); @@ -4171,6 +4200,8 @@ typedef void (wxEvtHandler::*wxPressAndTapEventFunction)(wxPressAndTapEvent&); wxEVENT_HANDLER_CAST(wxSysColourChangedEventFunction, func) #define wxDisplayChangedEventHandler(func) \ wxEVENT_HANDLER_CAST(wxDisplayChangedEventFunction, func) +#define wxDPIChangedEventHandler(func) \ + wxEVENT_HANDLER_CAST(wxDPIChangedEventFunction, func) #define wxUpdateUIEventHandler(func) \ wxEVENT_HANDLER_CAST(wxUpdateUIEventFunction, func) #define wxCloseEventHandler(func) \ @@ -4430,6 +4461,7 @@ typedef void (wxEvtHandler::*wxPressAndTapEventFunction)(wxPressAndTapEvent&); #define EVT_INIT_DIALOG(func) wx__DECLARE_EVT0(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(func)) #define EVT_SYS_COLOUR_CHANGED(func) wx__DECLARE_EVT0(wxEVT_SYS_COLOUR_CHANGED, wxSysColourChangedEventHandler(func)) #define EVT_DISPLAY_CHANGED(func) wx__DECLARE_EVT0(wxEVT_DISPLAY_CHANGED, wxDisplayChangedEventHandler(func)) +#define EVT_DPI_CHANGED(func) wx__DECLARE_EVT0(wxEVT_DPI_CHANGED, wxDPIChangedEventHandler(func)) #define EVT_SHOW(func) wx__DECLARE_EVT0(wxEVT_SHOW, wxShowEventHandler(func)) #define EVT_MAXIMIZE(func) wx__DECLARE_EVT0(wxEVT_MAXIMIZE, wxMaximizeEventHandler(func)) #define EVT_ICONIZE(func) wx__DECLARE_EVT0(wxEVT_ICONIZE, wxIconizeEventHandler(func)) diff --git a/include/wx/font.h b/include/wx/font.h index 4e986bb49f..409302d2e4 100644 --- a/include/wx/font.h +++ b/include/wx/font.h @@ -501,6 +501,13 @@ public: // account as well. static int GetNumericWeightOf(wxFontWeight weight); + // Some ports need to modify the font object when the DPI of the window it + // is used with changes, this function can be used to do it. + // + // Currently it is only used in wxMSW and is not considered to be part of + // wxWidgets public API. + virtual void WXAdjustToPPI(const wxSize& WXUNUSED(ppi)) { } + // this doesn't do anything and is kept for compatibility only #if WXWIN_COMPATIBILITY_2_8 wxDEPRECATED_INLINE(void SetNoAntiAliasing(bool no = true), wxUnusedVar(no);) diff --git a/include/wx/fontutil.h b/include/wx/fontutil.h index 2ae62eaf56..424c100136 100644 --- a/include/wx/fontutil.h +++ b/include/wx/fontutil.h @@ -120,25 +120,17 @@ public: #elif defined(__WXMSW__) wxNativeFontInfo(const LOGFONT& lf_) : lf(lf_), - pointSize(GetPointSizeFromLogFontHeight(lf.lfHeight)) + pointSize(GetPointSizeAtPPI(lf.lfHeight)) { } - // MSW-specific: get point size from LOGFONT height using the default DPI. - static float GetPointSizeFromLogFontHeight(int height); + // MSW-specific: get point size from LOGFONT height using specified DPI, + // or screen DPI when 0. + static float GetPointSizeAtPPI(int lfHeight, int ppi = 0); // MSW-specific: get the height value in pixels using LOGFONT convention // (i.e. negative) corresponding to the given size in points and DPI. - static int GetLogFontHeightAtPPI(float size, int ppi) - { - return -wxRound(size * ppi / 72.0); - } - - // And the same thing for the size of this font. - int GetLogFontHeightAtPPI(int ppi) const - { - return GetLogFontHeightAtPPI(pointSize, ppi); - } + static int GetLogFontHeightAtPPI(float size, int ppi); LOGFONT lf; diff --git a/include/wx/msw/font.h b/include/wx/msw/font.h index ee803d9cce..70ede34b88 100644 --- a/include/wx/msw/font.h +++ b/include/wx/msw/font.h @@ -120,6 +120,8 @@ public: virtual bool IsFixedWidth() const wxOVERRIDE; + virtual void WXAdjustToPPI(const wxSize& ppi) wxOVERRIDE; + wxDEPRECATED_MSG("use wxFONT{FAMILY,STYLE,WEIGHT}_XXX constants ie: wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD") wxFont(int size, int family, diff --git a/include/wx/msw/missing.h b/include/wx/msw/missing.h index e56a6bfa68..7f381bb898 100644 --- a/include/wx/msw/missing.h +++ b/include/wx/msw/missing.h @@ -23,7 +23,11 @@ #endif #ifndef WM_PRINTCLIENT - #define WM_PRINTCLIENT 0x318 + #define WM_PRINTCLIENT 0x0318 +#endif + +#ifndef WM_DPICHANGED + #define WM_DPICHANGED 0x02E0 #endif #ifndef DT_HIDEPREFIX diff --git a/include/wx/msw/toplevel.h b/include/wx/msw/toplevel.h index 0256104ebc..5cef6b1e8a 100644 --- a/include/wx/msw/toplevel.h +++ b/include/wx/msw/toplevel.h @@ -165,6 +165,9 @@ protected: int& x, int& y, 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 // next time and also indicates whether the window should be considered // being iconized or maximized (which may be different from whether it's @@ -191,6 +194,14 @@ protected: wxWindowRef m_winLastFocused; 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 PerMonitorV2. + bool m_perMonitorDPIaware; // The system menu: initially NULL but can be set (once) by // MSWGetSystemMenu(). Owned by this window. diff --git a/include/wx/msw/window.h b/include/wx/msw/window.h index 876d0d2b93..dd6c10a6b5 100644 --- a/include/wx/msw/window.h +++ b/include/wx/msw/window.h @@ -586,7 +586,17 @@ public: // Should be overridden by all classes storing the "last focused" window. 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 and generate + // wxDPIChangedEvent to let the user code do the same thing as well. + void MSWUpdateOnDPIChange(const wxSize& oldDPI, const wxSize& newDPI); + protected: + // Called from MSWUpdateOnDPIChange() specifically to update the control + // font, as this may need to be done differently for some specific native + // controls. The default version updates m_font of this window. + virtual void MSWUpdateFontOnDPIChange(const wxSize& newDPI); + // this allows you to implement standard control borders without // repeating the code in different classes that are not derived from // wxControl diff --git a/interface/wx/event.h b/interface/wx/event.h index 3b80a529fb..e9e124998d 100644 --- a/interface/wx/event.h +++ b/interface/wx/event.h @@ -3318,6 +3318,62 @@ public: }; + +/** + @class wxDPIChangedEvent + + Event sent when the resolution (measured in dots-per-inch, or DPI) of the + monitor a window is on changes. + + The event is sent to each wxTopLevelWindow affected by the change, and all + its children recursively. For example, this event is sent to the window + when it is moved, by the user, from a display using some DPI value to + another display using a different DPI value. It also sent to all program + windows on the given display if its DPI changes due to a change in the + system settings. + + Currently this event is generated by wxMSW port if only and only if the + MSW application runs under Windows 10 Creators Update (v1703) or later and + is marked as being "per-monitor DPI aware", i.e. contains a @c dpiAwareness + tag with the value "PerMonitorV2" in its manifest (see Microsoft + "Application Manifests" documentation + for more details). + + @note Per-monitor DPI support is an experimental feature that is still in + development. It might not work correctly for some controls. + + @beginEventTable{wxDPIChangedEvent} + @event{EVT_DPI_CHANGED(func)} + Process a @c wxEVT_DPI_CHANGED event. + @endEventTable + + @since 3.1.3 + + @library{wxcore} + @category{events} + + @see @ref overview_events +*/ +class wxDPIChangedEvent : public wxEvent +{ +public: + /** + Returns the old DPI. + + @since 3.1.3 + */ + wxSize GetOldDPI() const; + + /** + Returns the new DPI. + + @since 3.1.3 + */ + wxSize GetNewDPI() const; +}; + + + class wxPaletteChangedEvent : public wxEvent { public: diff --git a/interface/wx/window.h b/interface/wx/window.h index 8628ce5d26..d8d8fff28c 100644 --- a/interface/wx/window.h +++ b/interface/wx/window.h @@ -2044,7 +2044,7 @@ public: If the DPI is not available, returns @c wxSize(0,0) object. - @see wxDisplay::GetPPI() + @see wxDisplay::GetPPI(), wxDPIChangedEvent @since 3.1.3 */ diff --git a/src/common/event.cpp b/src/common/event.cpp index 14249ede37..d10275b49b 100644 --- a/src/common/event.cpp +++ b/src/common/event.cpp @@ -92,6 +92,7 @@ wxIMPLEMENT_DYNAMIC_CLASS(wxSetCursorEvent, wxEvent); wxIMPLEMENT_DYNAMIC_CLASS(wxSysColourChangedEvent, wxEvent); wxIMPLEMENT_DYNAMIC_CLASS(wxDisplayChangedEvent, wxEvent); + wxIMPLEMENT_DYNAMIC_CLASS(wxDPIChangedEvent, wxEvent); wxIMPLEMENT_DYNAMIC_CLASS(wxUpdateUIEvent, wxCommandEvent); wxIMPLEMENT_DYNAMIC_CLASS(wxNavigationKeyEvent, wxEvent); wxIMPLEMENT_DYNAMIC_CLASS(wxPaletteChangedEvent, wxEvent); @@ -302,6 +303,7 @@ wxDEFINE_EVENT( wxEVT_MENU_HIGHLIGHT, wxMenuEvent ); wxDEFINE_EVENT( wxEVT_CONTEXT_MENU, wxContextMenuEvent ); wxDEFINE_EVENT( wxEVT_SYS_COLOUR_CHANGED, wxSysColourChangedEvent ); wxDEFINE_EVENT( wxEVT_DISPLAY_CHANGED, wxDisplayChangedEvent ); +wxDEFINE_EVENT( wxEVT_DPI_CHANGED, wxDPIChangedEvent ); wxDEFINE_EVENT( wxEVT_QUERY_NEW_PALETTE, wxQueryNewPaletteEvent ); wxDEFINE_EVENT( wxEVT_PALETTE_CHANGED, wxPaletteChangedEvent ); wxDEFINE_EVENT( wxEVT_JOY_BUTTON_DOWN, wxJoystickEvent ); diff --git a/src/common/wincmn.cpp b/src/common/wincmn.cpp index 99c05f20cf..bc8a88139a 100644 --- a/src/common/wincmn.cpp +++ b/src/common/wincmn.cpp @@ -1726,6 +1726,8 @@ wxFont wxWindowBase::GetFont() const if ( !font.IsOk() ) font = GetClassDefaultAttributes().font; + font.WXAdjustToPPI(GetDPI()); + return font; } else @@ -1744,6 +1746,9 @@ bool wxWindowBase::SetFont(const wxFont& font) m_hasFont = font.IsOk(); m_inheritFont = m_hasFont; + if ( m_hasFont ) + m_font.WXAdjustToPPI(GetDPI()); + InvalidateBestSize(); return true; diff --git a/src/msw/font.cpp b/src/msw/font.cpp index e3e8012042..e83b07488a 100644 --- a/src/msw/font.cpp +++ b/src/msw/font.cpp @@ -159,7 +159,8 @@ public: int GetLogFontHeightAtPPI(int ppi) const { - return m_nativeFontInfo.GetLogFontHeightAtPPI(ppi); + return m_nativeFontInfo.GetLogFontHeightAtPPI( + m_nativeFontInfo.pointSize, ppi); } // ... and setters: notice that all of them invalidate the currently @@ -403,12 +404,18 @@ void wxFontRefData::Free() // ---------------------------------------------------------------------------- /* static */ -float wxNativeFontInfo::GetPointSizeFromLogFontHeight(int height) +float wxNativeFontInfo::GetPointSizeAtPPI(int lfHeight, int ppi) { - // Determine the size in points using the primary screen DPI as we don't - // have anything else here. - const float ppi = ::GetDeviceCaps(ScreenHDC(), LOGPIXELSY); - return 72.0f * abs(height) / ppi; + if ( ppi == 0 ) + ppi = ::GetDeviceCaps(ScreenHDC(), LOGPIXELSY); + + return abs(lfHeight) * 72.0f / ppi; +} + +/* static */ +int wxNativeFontInfo::GetLogFontHeightAtPPI(float size, int ppi) +{ + return -wxRound(size * ppi / 72.0f); } void wxNativeFontInfo::Init() @@ -512,7 +519,8 @@ wxFontEncoding wxNativeFontInfo::GetEncoding() const void wxNativeFontInfo::SetFractionalPointSize(float pointSizeNew) { // We don't have the correct DPI to use here, so use that of the - // primary screen. + // primary screen and rely on WXAdjustToPPI() changing it later if + // necessary. const int ppi = ::GetDeviceCaps(ScreenHDC(), LOGPIXELSY); lf.lfHeight = GetLogFontHeightAtPPI(pointSizeNew, ppi); @@ -535,7 +543,7 @@ void wxNativeFontInfo::SetPixelSize(const wxSize& pixelSize) // We don't have the right DPI to use here neither, but we need to update // the point size too, so fall back to the default. - pointSize = GetPointSizeFromLogFontHeight(lf.lfHeight); + pointSize = GetPointSizeAtPPI(lf.lfHeight); } void wxNativeFontInfo::SetStyle(wxFontStyle style) @@ -698,7 +706,7 @@ bool wxNativeFontInfo::FromString(const wxString& s) return false; lf.lfHeight = l; if ( setPointSizeFromHeight ) - pointSize = GetPointSizeFromLogFontHeight(l); + pointSize = GetPointSizeAtPPI(l); token = tokenizer.GetNextToken(); if ( !token.ToLong(&l) ) @@ -899,6 +907,19 @@ void wxFont::SetPixelSize(const wxSize& pixelSize) M_FONTDATA->SetPixelSize(pixelSize); } +void wxFont::WXAdjustToPPI(const wxSize& ppi) +{ + // We only use vertical component here as we only adjust LOGFONT::lfHeight. + const int heightNew = M_FONTDATA->GetLogFontHeightAtPPI(ppi.y); + + if ( heightNew != M_FONTDATA->GetLogFontHeight() ) + { + AllocExclusive(); + + M_FONTDATA->SetLogFontHeight(heightNew); + } +} + void wxFont::SetFamily(wxFontFamily family) { AllocExclusive(); diff --git a/src/msw/menuitem.cpp b/src/msw/menuitem.cpp index 83733eb4aa..cbf114bb9a 100644 --- a/src/msw/menuitem.cpp +++ b/src/msw/menuitem.cpp @@ -392,7 +392,15 @@ void MenuDrawData::Init() Offset = -12; - Font = wxFont(wxNativeFontInfo(metrics.lfMenuFont)); + wxNativeFontInfo info(metrics.lfMenuFont); + // wxNativeFontInfo constructor calculates the pointSize using the + // main screen DPI. But lfHeight is based on the window DPI. + if ( window ) + { + info.pointSize = wxNativeFontInfo::GetPointSizeAtPPI( + info.lf.lfHeight, window->GetDPI().y); + } + Font = wxFont(info); Theme = false; } diff --git a/src/msw/msgdlg.cpp b/src/msw/msgdlg.cpp index 8e2a101a55..21fbfc4e4b 100644 --- a/src/msw/msgdlg.cpp +++ b/src/msw/msgdlg.cpp @@ -397,8 +397,17 @@ void wxMessageDialog::AdjustButtonLabels() wxFont wxMessageDialog::GetMessageFont() { const wxWindow* win = wxTheApp ? wxTheApp->GetTopWindow() : NULL; - const NONCLIENTMETRICS& ncm = wxMSWImpl::GetNonClientMetrics(win); - return wxNativeFontInfo(ncm.lfMessageFont); + wxNativeFontInfo info(wxMSWImpl::GetNonClientMetrics(win).lfMessageFont); + + // wxNativeFontInfo constructor calculates the pointSize using the + // main screen DPI. But lfHeight is based on the window DPI. + if ( win ) + { + info.pointSize = wxNativeFontInfo::GetPointSizeAtPPI( + info.lf.lfHeight, win->GetDPI().y); + } + + return info; } int wxMessageDialog::ShowMessageBox() diff --git a/src/msw/settings.cpp b/src/msw/settings.cpp index fc1783c1ac..c2029cc67f 100644 --- a/src/msw/settings.cpp +++ b/src/msw/settings.cpp @@ -183,8 +183,17 @@ wxFont wxSystemSettingsNative::GetFont(wxSystemFont index) // controls may prefer to use lfStatusFont or lfCaptionFont if it // is more appropriate for them const wxWindow* win = wxTheApp ? wxTheApp->GetTopWindow() : NULL; - const wxNativeFontInfo + wxNativeFontInfo info(wxMSWImpl::GetNonClientMetrics(win).lfMessageFont); + + // wxNativeFontInfo constructor calculates the pointSize using the + // main screen DPI. But lfHeight is based on the window DPI. + if ( win ) + { + info.pointSize = wxNativeFontInfo::GetPointSizeAtPPI( + info.lf.lfHeight, win->GetDPI().y); + } + gs_fontDefault = new wxFont(info); } diff --git a/src/msw/toplevel.cpp b/src/msw/toplevel.cpp index 5939b9636f..d2355b0179 100644 --- a/src/msw/toplevel.cpp +++ b/src/msw/toplevel.cpp @@ -104,6 +104,9 @@ void wxTopLevelWindowMSW::Init() m_fsIsShowing = false; m_menuSystem = NULL; + + m_activeDPI = wxDefaultSize; + m_perMonitorDPIaware = false; } WXDWORD wxTopLevelWindowMSW::MSWGetStyle(long style, WXDWORD *exflags) const @@ -246,6 +249,26 @@ WXHWND wxTopLevelWindowMSW::MSWGetParent() const 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 rc = 0; @@ -306,6 +329,17 @@ WXLRESULT wxTopLevelWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WX #endif // #ifndef __WXUNIVERSAL__ } break; + + case WM_DPICHANGED: + { + const RECT* const prcNewWindow = + reinterpret_cast(lParam); + + processed = HandleDPIChange(wxSize(LOWORD(wParam), + HIWORD(wParam)), + wxRectFromRECT(*prcNewWindow)); + } + break; } if ( !processed ) @@ -314,6 +348,47 @@ WXLRESULT wxTopLevelWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WX 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, const wxString& title, const wxPoint& pos, @@ -484,6 +559,10 @@ bool wxTopLevelWindowMSW::Create(wxWindow *parent, EnableCloseButton(false); } + m_activeDPI = GetDPI(); + + m_perMonitorDPIaware = IsPerMonitorDPIAware(GetHwnd()); + // for standard dialogs the dialog manager generates WM_CHANGEUISTATE // itself but for custom windows we have to do it ourselves in order to // make the keyboard indicators (such as underlines for accelerators and diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 77a883460b..f2ca075797 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -4837,6 +4837,65 @@ wxSize wxWindowMSW::GetDPI() const return dpi; } +void wxWindowMSW::MSWUpdateFontOnDPIChange(const wxSize& newDPI) +{ + if ( m_font.IsOk() ) + { + m_font.WXAdjustToPPI(newDPI); + + // WXAdjustToPPI() changes the HFONT, so reassociate it with the window. + wxSetWindowFont(GetHwnd(), m_font); + } +} + +// 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 font if necessary + MSWUpdateFontOnDPIChange(newDPI); + + // 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(); + } + + wxDPIChangedEvent event(oldDPI, newDPI); + event.SetEventObject(this); + HandleWindowEvent(event); +} + // --------------------------------------------------------------------------- // colours and palettes // ---------------------------------------------------------------------------