Merge branch 'win32-native-dialogs-pmdpi' of https://github.com/MaartenBent/wxWidgets

Improve native MSW dialog DPI awareness.

See #24196.
This commit is contained in:
Vadim Zeitlin 2024-01-19 02:43:11 +01:00
commit 5ae3b771ad
3 changed files with 50 additions and 36 deletions

View file

@ -15,6 +15,8 @@
#endif
#include "wx/dynlib.h"
#include "wx/display.h"
#include "wx/sysopt.h"
namespace wxMSWImpl
{
@ -35,6 +37,9 @@ public:
AutoSystemDpiAware()
: m_prevContext(WXDPI_AWARENESS_CONTEXT_UNAWARE)
{
if ( !Needed() )
return;
if ( ms_pfnSetThreadDpiAwarenessContext == (SetThreadDpiAwarenessContext_t)-1)
{
wxLoadedDLL dllUser32("user32.dll");
@ -62,6 +67,20 @@ public:
}
}
static bool Needed()
{
// use system-dpi-aware context when:
// - the user did not set an option to force per-monitor context
// - there are displays with different DPI
if ( wxSystemOptions::GetOptionInt("msw.native-dialogs-pmdpi") == 1 )
return false;
bool diferentDPI = false;
for ( unsigned i = 1; i < wxDisplay::GetCount() && !diferentDPI; ++i )
diferentDPI = wxDisplay(0u).GetPPI() != wxDisplay(i).GetPPI();
return diferentDPI;
}
private:
WXDPI_AWARENESS_CONTEXT m_prevContext;

View file

@ -81,6 +81,12 @@
using it, i.e. this has the same effect as calling
wxApp::MSWEnableDarkMode(). If set to 2, use dark mode unconditionally,
as if this function were called with wxApp::DarkMode_Always argument.
@flag{msw.native-dialogs-pmdpi}
Some native win32 dialogs (like the font and colour pickers) are not
per-monitor DPI aware, and wxWidgets will forcefully show them as
system DPI aware when there are monitors with different DPI connected.
If set to 1, these dialogs will always be shown as per-monitor DPI
aware (when enabled in the manifest).
@endFlagTable

View file

@ -126,21 +126,22 @@ int wxFontDialog::ShowModal()
chooseFontStruct.lpfnHook = wxFontDialogHookProc;
}
// The native font dialog does not support moving between displays with
// different DPIs. Check if it will be shown system-dpi-aware.
const bool useSystemDPI = wxMSWImpl::AutoSystemDpiAware::Needed();
// When the font dialog is system-dpi-aware, it expects the font at 96DPI/100% scaling.
// When the font dialog is per-monitor-dpi-aware, it expects a font with the system DPI.
const int fontdlgDPI = useSystemDPI ? wxDisplay::GetStdPPIValue() : wxGetDPIofHDC(ScreenHDC()).y;
if ( m_fontData.m_initialFont.IsOk() )
{
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 either) DPI.
logFont.lfHeight = wxNativeFontInfo::GetLogFontHeightAtPPI
(
m_fontData.m_initialFont.GetFractionalPointSize(),
wxDisplay::GetStdPPIValue()
);
// Convert the DPI of the font to the DPI of the font dialog.
const double fPointSize = m_fontData.m_initialFont.GetFractionalPointSize();
logFont.lfHeight = wxNativeFontInfo::GetLogFontHeightAtPPI(fPointSize, fontdlgDPI);
}
if ( m_fontData.m_fontColour.IsOk() )
@ -180,33 +181,21 @@ int wxFontDialog::ShowModal()
// 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()
));
// 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.
//
// Convert from the DPI of the font dialog to the DPI the
// wxNativeFontInfo constructor will use to determine the font size.
const double fPointSize = wxNativeFontInfo::GetPointSizeAtPPI(logFont.lfHeight, fontdlgDPI);
const int fontDPI = wxGetDPIofHDC(ScreenHDC()).y;
logFont.lfHeight = wxNativeFontInfo::GetLogFontHeightAtPPI(wxRound(fPointSize), fontDPI);
// 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
);
}
wxFont f(wxNativeFontInfo(logFont, parent));
// Use nullptr, so the pointSize calculation in wxNativeFontInfo will
// use the same fontDPI as is used above for lfHeight.
wxFont f(wxNativeFontInfo(logFont, nullptr));
// The native dialog allows selecting only integer font sizes in
// points, but converting them to pixel height loses precision and so