From 9b25ed038afbbfad9581263a5f65232fabb02879 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 16 Apr 2022 16:40:49 +0100 Subject: [PATCH 1/5] Add an accelerator for the font dialog in the dialogs sample No real changes, just make it faster to bring up the font dialog for testing. --- samples/dialogs/dialogs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/dialogs/dialogs.cpp b/samples/dialogs/dialogs.cpp index 637e999db4..c12adb2731 100644 --- a/samples/dialogs/dialogs.cpp +++ b/samples/dialogs/dialogs.cpp @@ -432,7 +432,7 @@ bool MyApp::OnInit() #endif // wxUSE_COLOURDLG #if wxUSE_FONTDLG - choices_menu->Append(DIALOGS_CHOOSE_FONT, "Choose &font"); + choices_menu->Append(DIALOGS_CHOOSE_FONT, "Choose &font\tShift-Ctrl-N"); #endif // wxUSE_FONTDLG #if wxUSE_CHOICEDLG From caac3a4f193a913e87614a922b35993ce585fad7 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 16 Apr 2022 17:45:29 +0100 Subject: [PATCH 2/5] Load SetThreadDpiAwarenessContext() function pointer only once A small optimization to avoid resolving this function dynamically every time it is needed and just do it once, on first use. --- include/wx/msw/private/dpiaware.h | 20 ++++++++++---------- src/msw/window.cpp | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/include/wx/msw/private/dpiaware.h b/include/wx/msw/private/dpiaware.h index cab54e8af2..d7a22ed7b0 100644 --- a/include/wx/msw/private/dpiaware.h +++ b/include/wx/msw/private/dpiaware.h @@ -35,22 +35,21 @@ class AutoSystemDpiAware public: AutoSystemDpiAware() - : m_prevContext(WXDPI_AWARENESS_CONTEXT_UNAWARE), - m_pfnSetThreadDpiAwarenessContext((SetThreadDpiAwarenessContext_t)-1) + : m_prevContext(WXDPI_AWARENESS_CONTEXT_UNAWARE) { - if ( m_pfnSetThreadDpiAwarenessContext == (SetThreadDpiAwarenessContext_t)-1) + if ( ms_pfnSetThreadDpiAwarenessContext == (SetThreadDpiAwarenessContext_t)-1) { wxLoadedDLL dllUser32("user32.dll"); - wxDL_INIT_FUNC(m_pfn, SetThreadDpiAwarenessContext, dllUser32); + wxDL_INIT_FUNC(ms_pfn, SetThreadDpiAwarenessContext, dllUser32); } - if ( m_pfnSetThreadDpiAwarenessContext ) + if ( ms_pfnSetThreadDpiAwarenessContext ) { - m_prevContext = m_pfnSetThreadDpiAwarenessContext( + m_prevContext = ms_pfnSetThreadDpiAwarenessContext( WXDPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED); if ( !m_prevContext ) { - m_prevContext = m_pfnSetThreadDpiAwarenessContext( + m_prevContext = ms_pfnSetThreadDpiAwarenessContext( WXDPI_AWARENESS_CONTEXT_SYSTEM_AWARE); } } @@ -59,16 +58,17 @@ public: ~AutoSystemDpiAware() { - if ( m_pfnSetThreadDpiAwarenessContext ) + if ( ms_pfnSetThreadDpiAwarenessContext ) { - m_pfnSetThreadDpiAwarenessContext(m_prevContext); + ms_pfnSetThreadDpiAwarenessContext(m_prevContext); } } private: WXDPI_AWARENESS_CONTEXT m_prevContext; - SetThreadDpiAwarenessContext_t m_pfnSetThreadDpiAwarenessContext; + // This static member is defined in src/msw/window.cpp. + static SetThreadDpiAwarenessContext_t ms_pfnSetThreadDpiAwarenessContext; }; #else // !wxUSE_DYNLIB_CLASS diff --git a/src/msw/window.cpp b/src/msw/window.cpp index afc5cb8518..784c952f52 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -76,6 +76,7 @@ #endif #include "wx/msw/private.h" +#include "wx/msw/private/dpiaware.h" #include "wx/msw/private/keyboard.h" #include "wx/msw/private/paint.h" #include "wx/msw/private/winstyle.h" @@ -4758,6 +4759,19 @@ wxWindowMSW::MSWOnMeasureItem(int id, WXMEASUREITEMSTRUCT *itemStruct) // DPI // --------------------------------------------------------------------------- +#if wxUSE_DYNLIB_CLASS + +namespace wxMSWImpl +{ + +AutoSystemDpiAware::SetThreadDpiAwarenessContext_t +AutoSystemDpiAware::ms_pfnSetThreadDpiAwarenessContext = + (AutoSystemDpiAware::SetThreadDpiAwarenessContext_t)-1; + +} // namespace wxMSWImpl + +#endif // wxUSE_DYNLIB_CLASS + namespace { From f68993111cd7dfc81e2beb977bd513981067df33 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 17 Apr 2022 16:53:42 +0100 Subject: [PATCH 3/5] Change DPI awareness only for ChooseFont() duration Extract ChooseFont() in a small helper function to ensure that the DPI awareness is restored as soon as it returns. No real changes yet. --- src/msw/fontdlg.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/msw/fontdlg.cpp b/src/msw/fontdlg.cpp index c70b55ac50..253d65f4d2 100644 --- a/src/msw/fontdlg.cpp +++ b/src/msw/fontdlg.cpp @@ -88,6 +88,14 @@ wxString wxFontDialog::GetTitle() const return m_title; } +// Tiny wrapper calling ::ChooseFont() with system DPI awareness, as the +// standard dialog doesn't work correctly when using per-monitor awareness. +static BOOL wxMSWChooseFont(CHOOSEFONT* pCF) +{ + wxMSWImpl::AutoSystemDpiAware dpiAwareness; + return ::ChooseFont(pCF); +} + int wxFontDialog::ShowModal() { WX_HOOK_MODAL_DIALOG(); @@ -153,9 +161,7 @@ int wxFontDialog::ShowModal() chooseFontStruct.Flags = flags; - wxMSWImpl::AutoSystemDpiAware dpiAwareness; - - if ( ChooseFont(&chooseFontStruct) != 0 ) + if ( wxMSWChooseFont(&chooseFontStruct) != 0 ) { wxRGBToColour(m_fontData.m_fontColour, chooseFontStruct.rgbColors); m_fontData.m_chosenFont = wxFont(wxNativeFontInfo(logFont, this)); From 91f1be23dd72c657edea834d75db0690d48fcbbb Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 16 Apr 2022 17:32:33 +0100 Subject: [PATCH 4/5] Fix initial point size in MSW wxFontDialog in high DPI The native dialog doesn't take the actual DPI into account, so convert our font sizes that do use the DPI to the standard one before using it and convert font height back to the value corresponding to the actual DPI once a font is selected in it. Closes #22313. --- src/msw/fontdlg.cpp | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/msw/fontdlg.cpp b/src/msw/fontdlg.cpp index 253d65f4d2..0bd56a053d 100644 --- a/src/msw/fontdlg.cpp +++ b/src/msw/fontdlg.cpp @@ -34,6 +34,7 @@ #endif #include "wx/fontutil.h" +#include "wx/display.h" #include "wx/msw/private/dpiaware.h" #include @@ -129,6 +130,17 @@ int wxFontDialog::ShowModal() { flags |= CF_INITTOLOGFONTSTRUCT; logFont = m_fontData.m_initialFont.GetNativeFontInfo()->lf; + + // The standard dialog seems to always use the default DPI for + // converting LOGFONT height to the value in points shown in the + // dialog (and this happens even when not using AutoSystemDpiAware), + // so we need to convert it to standard (not even system, because the + // dialog doesn't take it into account neither) DPI. + logFont.lfHeight = wxNativeFontInfo::GetLogFontHeightAtPPI + ( + m_fontData.m_initialFont.GetFractionalPointSize(), + wxDisplay::GetStdPPIValue() + ); } if ( m_fontData.m_fontColour.IsOk() ) @@ -164,7 +176,37 @@ int wxFontDialog::ShowModal() if ( wxMSWChooseFont(&chooseFontStruct) != 0 ) { wxRGBToColour(m_fontData.m_fontColour, chooseFontStruct.rgbColors); - m_fontData.m_chosenFont = wxFont(wxNativeFontInfo(logFont, this)); + + // Don't trust the LOGFONT height returned by the native dialog because + // it doesn't use the correct DPI. + // + // Note that we must use our parent and not this window itself, as it + // doesn't have any valid HWND and so its DPI can't be determined. + if ( parent ) + { + // We can't just adjust lfHeight directly to the correct DPI here + // as doing this would introduce rounding problems, e.g. 8pt font + // corresponds to lfHeight == 11px and scaling this up for 150% DPI + // would result in 17px height which would then map to 8.5pt at + // 150% DPI and end up being rounded to 9pt, which would be wrong. + // + // So find the point size itself first: + const int pointSize = wxRound(wxNativeFontInfo::GetPointSizeAtPPI + ( + logFont.lfHeight, + wxDisplay::GetStdPPIValue() + )); + + // And then compute the pixel height that results in this point + // size at the actual DPI being used. + logFont.lfHeight = wxNativeFontInfo::GetLogFontHeightAtPPI + ( + pointSize, + parent->GetDPI().y + ); + } + + m_fontData.m_chosenFont = wxFont(wxNativeFontInfo(logFont, parent)); m_fontData.EncodingInfo().facename = logFont.lfFaceName; m_fontData.EncodingInfo().charset = logFont.lfCharSet; From 87a0472485c0cb567252b9d29136c722b1f76241 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 18 Apr 2022 15:45:10 +0100 Subject: [PATCH 5/5] Fix size of the font returned from wxFontDialog in wxMSW Ensure that the font has exactly the point size entered in the dialog, rather than having a fractional point size close, but not identical, to it, as happened before due to first rounding the result of converting the point size to pixels and then _not_ rounding the result of the reverse conversion. This resulted in wxFont::GetPointSize() returning the correct value, but not GetFractionalPointSize(). Now the latter returns the same, correctly rounded, value too. --- src/msw/fontdlg.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/msw/fontdlg.cpp b/src/msw/fontdlg.cpp index 0bd56a053d..aef54a2683 100644 --- a/src/msw/fontdlg.cpp +++ b/src/msw/fontdlg.cpp @@ -206,7 +206,18 @@ int wxFontDialog::ShowModal() ); } - m_fontData.m_chosenFont = wxFont(wxNativeFontInfo(logFont, parent)); + wxFont f(wxNativeFontInfo(logFont, parent)); + + // The native dialog allows selecting only integer font sizes in + // points, but converting them to pixel height loses precision and so + // converting them back to points may result in a fractional value + // different from the value selected in the dialog. So ensure that we + // use exactly the same font size in points as what was selected in the + // dialog by rounding the possibly fractional value to the integer ones + // entered there. + f.SetPointSize(wxRound(f.GetFractionalPointSize())); + + m_fontData.m_chosenFont = f; m_fontData.EncodingInfo().facename = logFont.lfFaceName; m_fontData.EncodingInfo().charset = logFont.lfCharSet;