Add high-DPI scale factor rounding policy C++ API

This API enables tuning of how Qt rounds fractional scale factors, and
corresponds to the QT_SCALE_FACTOR_ROUNDING_POLICY environment
variable

New API:
	Qt::HighDPiScaleFactorRoundingPolicy
	QGuiApplication::setHighDpiScaleFactorRoundingPolicy()
	QGuiApplication::highDpiScaleFactorRoundingPolicy()

Done-with: Friedemann Kleint <Friedemann.Kleint@qt.io>
Task-number: QTBUG-53022
Change-Id: Ic360f26a173caa757e4ebde35ce08a6b74290b7d
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
This commit is contained in:
Morten Johan Sørvig 2017-10-07 01:35:29 +02:00
parent 1de8b01d2b
commit f1e40dd6d6
7 changed files with 106 additions and 31 deletions

View File

@ -1754,6 +1754,15 @@ public:
ChecksumItuV41 ChecksumItuV41
}; };
enum class HighDpiScaleFactorRoundingPolicy {
Unset,
Round,
Ceil,
Floor,
RoundPreferFloor,
PassThrough
};
#ifndef Q_QDOC #ifndef Q_QDOC
// NOTE: Generally, do not add QT_Q_ENUM if a corresponding Q_Q_FLAG exists. // NOTE: Generally, do not add QT_Q_ENUM if a corresponding Q_Q_FLAG exists.
QT_Q_ENUM(ScrollBarPolicy) QT_Q_ENUM(ScrollBarPolicy)
@ -1840,6 +1849,7 @@ public:
QT_Q_ENUM(MouseEventSource) QT_Q_ENUM(MouseEventSource)
QT_Q_FLAG(MouseEventFlag) QT_Q_FLAG(MouseEventFlag)
QT_Q_ENUM(ChecksumType) QT_Q_ENUM(ChecksumType)
QT_Q_ENUM(HighDpiScaleFactorRoundingPolicy)
QT_Q_ENUM(TabFocusBehavior) QT_Q_ENUM(TabFocusBehavior)
#endif // Q_DOC #endif // Q_DOC

View File

@ -3275,3 +3275,25 @@
\value ChecksumItuV41 Checksum calculation based on ITU-V.41. \value ChecksumItuV41 Checksum calculation based on ITU-V.41.
*/ */
/*!
\enum Qt::HighDpiScaleFactorRoundingPolicy
\since 5.14
This enum describes the possible High-DPI scale factor rounding policies, which
decide how non-integer scale factors (such as Windows 150%) are handled.
The active policy is set by calling QGuiApplication::setHighDdpiScaleFactorRoundingPolicy() before
the application object is created, or by setting the QT_SCALE_FACTOR_ROUNDING_POLICY
environment variable.
\sa QGuiApplication::setHighDdpiScaleFactorRoundingPolicy()
\sa AA_EnableHighDpiScaling.
\omitvalue Unset
\value Round Round up for .5 and above.
\value Ceil Always round up.
\value Floor Always round down.
\value RoundPreferFloor Round up for .75 and above.
\value PassThrough Don't round.
*/

View File

@ -146,6 +146,8 @@ QString QGuiApplicationPrivate::styleOverride;
Qt::ApplicationState QGuiApplicationPrivate::applicationState = Qt::ApplicationInactive; Qt::ApplicationState QGuiApplicationPrivate::applicationState = Qt::ApplicationInactive;
Qt::HighDpiScaleFactorRoundingPolicy QGuiApplicationPrivate::highDpiScaleFactorRoundingPolicy =
Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor;
bool QGuiApplicationPrivate::highDpiScalingUpdated = false; bool QGuiApplicationPrivate::highDpiScalingUpdated = false;
QPointer<QWindow> QGuiApplicationPrivate::currentDragWindow; QPointer<QWindow> QGuiApplicationPrivate::currentDragWindow;
@ -687,6 +689,8 @@ QGuiApplication::~QGuiApplication()
QGuiApplicationPrivate::lastCursorPosition = {qInf(), qInf()}; QGuiApplicationPrivate::lastCursorPosition = {qInf(), qInf()};
QGuiApplicationPrivate::currentMousePressWindow = QGuiApplicationPrivate::currentMouseWindow = nullptr; QGuiApplicationPrivate::currentMousePressWindow = QGuiApplicationPrivate::currentMouseWindow = nullptr;
QGuiApplicationPrivate::applicationState = Qt::ApplicationInactive; QGuiApplicationPrivate::applicationState = Qt::ApplicationInactive;
QGuiApplicationPrivate::highDpiScaleFactorRoundingPolicy =
Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor;
QGuiApplicationPrivate::highDpiScalingUpdated = false; QGuiApplicationPrivate::highDpiScalingUpdated = false;
QGuiApplicationPrivate::currentDragWindow = nullptr; QGuiApplicationPrivate::currentDragWindow = nullptr;
QGuiApplicationPrivate::tabletDevicePoints.clear(); QGuiApplicationPrivate::tabletDevicePoints.clear();
@ -3490,6 +3494,46 @@ Qt::ApplicationState QGuiApplication::applicationState()
return QGuiApplicationPrivate::applicationState; return QGuiApplicationPrivate::applicationState;
} }
/*!
\since 5.14
Sets the high-DPI scale factor rounding policy for the application. The
policy decides how non-integer scale factors (such as Windows 150%) are
handled, for applications that have AA_EnableHighDpiScaling enabled.
The two principal options are whether fractional scale factors should
be rounded to an integer or not. Keeping the scale factor as-is will
make the user interface size match the OS setting exactly, but may cause
painting errors, for example with the Windows style.
If rounding is wanted, then which type of rounding should be decided
next. Mathematically correct rounding is supported but may not give
the best visual results: Consider if you want to render 1.5x as 1x
("small UI") or as 2x ("large UI"). See the Qt::HighDpiScaleFactorRoundingPolicy
enum for a complete list of all options.
This function must be called before creating the application object,
and can be overridden by setting the QT_SCALE_FACTOR_ROUNDING_POLICY
environment variable. The QGuiApplication::highDpiScaleFactorRoundingPolicy()
accessor will reflect the environment, if set.
The default value is Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor.
*/
void QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy policy)
{
QGuiApplicationPrivate::highDpiScaleFactorRoundingPolicy = policy;
}
/*!
\since 5.14
Returns the high-DPI scale factor rounding policy.
*/
Qt::HighDpiScaleFactorRoundingPolicy QGuiApplication::highDpiScaleFactorRoundingPolicy()
{
return QGuiApplicationPrivate::highDpiScaleFactorRoundingPolicy;
}
/*! /*!
\since 5.2 \since 5.2
\fn void QGuiApplication::applicationStateChanged(Qt::ApplicationState state) \fn void QGuiApplication::applicationStateChanged(Qt::ApplicationState state)

View File

@ -156,6 +156,9 @@ public:
static Qt::ApplicationState applicationState(); static Qt::ApplicationState applicationState();
static void setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy policy);
static Qt::HighDpiScaleFactorRoundingPolicy highDpiScaleFactorRoundingPolicy();
static int exec(); static int exec();
bool notify(QObject *, QEvent *) override; bool notify(QObject *, QEvent *) override;

View File

@ -223,6 +223,7 @@ public:
static QWindow *currentMouseWindow; static QWindow *currentMouseWindow;
static QWindow *currentMousePressWindow; static QWindow *currentMousePressWindow;
static Qt::ApplicationState applicationState; static Qt::ApplicationState applicationState;
static Qt::HighDpiScaleFactorRoundingPolicy highDpiScaleFactorRoundingPolicy;
static bool highDpiScalingUpdated; static bool highDpiScalingUpdated;
static QPointer<QWindow> currentDragWindow; static QPointer<QWindow> currentDragWindow;

View File

@ -331,24 +331,24 @@ static QByteArray joinEnumValues(const EnumLookup<EnumType> *i1, const EnumLooku
return result; return result;
} }
using ScaleFactorRoundingPolicyLookup = EnumLookup<QHighDpiScaling::HighDpiScaleFactorRoundingPolicy>; using ScaleFactorRoundingPolicyLookup = EnumLookup<Qt::HighDpiScaleFactorRoundingPolicy>;
static const ScaleFactorRoundingPolicyLookup scaleFactorRoundingPolicyLookup[] = static const ScaleFactorRoundingPolicyLookup scaleFactorRoundingPolicyLookup[] =
{ {
{"Round", QHighDpiScaling::HighDpiScaleFactorRoundingPolicy::Round}, {"Round", Qt::HighDpiScaleFactorRoundingPolicy::Round},
{"Ceil", QHighDpiScaling::HighDpiScaleFactorRoundingPolicy::Ceil}, {"Ceil", Qt::HighDpiScaleFactorRoundingPolicy::Ceil},
{"Floor", QHighDpiScaling::HighDpiScaleFactorRoundingPolicy::Floor}, {"Floor", Qt::HighDpiScaleFactorRoundingPolicy::Floor},
{"RoundPreferFloor", QHighDpiScaling::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor}, {"RoundPreferFloor", Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor},
{"PassThrough", QHighDpiScaling::HighDpiScaleFactorRoundingPolicy::PassThrough} {"PassThrough", Qt::HighDpiScaleFactorRoundingPolicy::PassThrough}
}; };
static QHighDpiScaling::HighDpiScaleFactorRoundingPolicy static Qt::HighDpiScaleFactorRoundingPolicy
lookupScaleFactorRoundingPolicy(const QByteArray &v) lookupScaleFactorRoundingPolicy(const QByteArray &v)
{ {
auto end = std::end(scaleFactorRoundingPolicyLookup); auto end = std::end(scaleFactorRoundingPolicyLookup);
auto it = std::find(std::begin(scaleFactorRoundingPolicyLookup), end, auto it = std::find(std::begin(scaleFactorRoundingPolicyLookup), end,
ScaleFactorRoundingPolicyLookup{v.constData(), QHighDpiScaling::HighDpiScaleFactorRoundingPolicy::Unset}); ScaleFactorRoundingPolicyLookup{v.constData(), Qt::HighDpiScaleFactorRoundingPolicy::Unset});
return it != end ? it->value : QHighDpiScaling::HighDpiScaleFactorRoundingPolicy::Unset; return it != end ? it->value : Qt::HighDpiScaleFactorRoundingPolicy::Unset;
} }
using DpiAdjustmentPolicyLookup = EnumLookup<QHighDpiScaling::DpiAdjustmentPolicy>; using DpiAdjustmentPolicyLookup = EnumLookup<QHighDpiScaling::DpiAdjustmentPolicy>;
@ -377,15 +377,15 @@ qreal QHighDpiScaling::roundScaleFactor(qreal rawFactor)
// sizes that are smaller than the ideal size, and opposite for rounding up. // sizes that are smaller than the ideal size, and opposite for rounding up.
// Rounding down is then preferable since "small UI" is a more acceptable // Rounding down is then preferable since "small UI" is a more acceptable
// high-DPI experience than "large UI". // high-DPI experience than "large UI".
static auto scaleFactorRoundingPolicy = HighDpiScaleFactorRoundingPolicy::Unset; static auto scaleFactorRoundingPolicy = Qt::HighDpiScaleFactorRoundingPolicy::Unset;
// Determine rounding policy // Determine rounding policy
if (scaleFactorRoundingPolicy == HighDpiScaleFactorRoundingPolicy::Unset) { if (scaleFactorRoundingPolicy == Qt::HighDpiScaleFactorRoundingPolicy::Unset) {
// Check environment // Check environment
if (qEnvironmentVariableIsSet(scaleFactorRoundingPolicyEnvVar)) { if (qEnvironmentVariableIsSet(scaleFactorRoundingPolicyEnvVar)) {
QByteArray policyText = qgetenv(scaleFactorRoundingPolicyEnvVar); QByteArray policyText = qgetenv(scaleFactorRoundingPolicyEnvVar);
auto policyEnumValue = lookupScaleFactorRoundingPolicy(policyText); auto policyEnumValue = lookupScaleFactorRoundingPolicy(policyText);
if (policyEnumValue != HighDpiScaleFactorRoundingPolicy::Unset) { if (policyEnumValue != Qt::HighDpiScaleFactorRoundingPolicy::Unset) {
scaleFactorRoundingPolicy = policyEnumValue; scaleFactorRoundingPolicy = policyEnumValue;
} else { } else {
auto values = joinEnumValues(std::begin(scaleFactorRoundingPolicyLookup), auto values = joinEnumValues(std::begin(scaleFactorRoundingPolicyLookup),
@ -393,38 +393,43 @@ qreal QHighDpiScaling::roundScaleFactor(qreal rawFactor)
qWarning("Unknown scale factor rounding policy: %s. Supported values are: %s.", qWarning("Unknown scale factor rounding policy: %s. Supported values are: %s.",
policyText.constData(), values.constData()); policyText.constData(), values.constData());
} }
}
// Check application object if no environment value was set.
if (scaleFactorRoundingPolicy == Qt::HighDpiScaleFactorRoundingPolicy::Unset) {
scaleFactorRoundingPolicy = QGuiApplication::highDpiScaleFactorRoundingPolicy();
} else { } else {
// Set default policy if no environment variable is set. // Make application setting reflect environment
scaleFactorRoundingPolicy = HighDpiScaleFactorRoundingPolicy::RoundPreferFloor; QGuiApplication::setHighDpiScaleFactorRoundingPolicy(scaleFactorRoundingPolicy);
} }
} }
// Apply rounding policy. // Apply rounding policy.
qreal roundedFactor = rawFactor; qreal roundedFactor = rawFactor;
switch (scaleFactorRoundingPolicy) { switch (scaleFactorRoundingPolicy) {
case HighDpiScaleFactorRoundingPolicy::Round: case Qt::HighDpiScaleFactorRoundingPolicy::Round:
roundedFactor = qRound(rawFactor); roundedFactor = qRound(rawFactor);
break; break;
case HighDpiScaleFactorRoundingPolicy::Ceil: case Qt::HighDpiScaleFactorRoundingPolicy::Ceil:
roundedFactor = qCeil(rawFactor); roundedFactor = qCeil(rawFactor);
break; break;
case HighDpiScaleFactorRoundingPolicy::Floor: case Qt::HighDpiScaleFactorRoundingPolicy::Floor:
roundedFactor = qFloor(rawFactor); roundedFactor = qFloor(rawFactor);
break; break;
case HighDpiScaleFactorRoundingPolicy::RoundPreferFloor: case Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor:
// Round up for .75 and higher. This favors "small UI" over "large UI". // Round up for .75 and higher. This favors "small UI" over "large UI".
roundedFactor = rawFactor - qFloor(rawFactor) < 0.75 roundedFactor = rawFactor - qFloor(rawFactor) < 0.75
? qFloor(rawFactor) : qCeil(rawFactor); ? qFloor(rawFactor) : qCeil(rawFactor);
break; break;
case HighDpiScaleFactorRoundingPolicy::PassThrough: case Qt::HighDpiScaleFactorRoundingPolicy::PassThrough:
case HighDpiScaleFactorRoundingPolicy::Unset: case Qt::HighDpiScaleFactorRoundingPolicy::Unset:
break; break;
} }
// Don't round down to to zero; clamp the minimum (rounded) factor to 1. // Don't round down to to zero; clamp the minimum (rounded) factor to 1.
// This is not a common case but can happen if a display reports a very // This is not a common case but can happen if a display reports a very
// low DPI. // low DPI.
if (scaleFactorRoundingPolicy != HighDpiScaleFactorRoundingPolicy::PassThrough) if (scaleFactorRoundingPolicy != Qt::HighDpiScaleFactorRoundingPolicy::PassThrough)
roundedFactor = qMax(roundedFactor, qreal(1)); roundedFactor = qMax(roundedFactor, qreal(1));
return roundedFactor; return roundedFactor;

View File

@ -74,16 +74,6 @@ typedef QPair<qreal, qreal> QDpi;
class Q_GUI_EXPORT QHighDpiScaling { class Q_GUI_EXPORT QHighDpiScaling {
Q_GADGET Q_GADGET
public: public:
enum class HighDpiScaleFactorRoundingPolicy {
Unset,
Round,
Ceil,
Floor,
RoundPreferFloor,
PassThrough
};
Q_ENUM(HighDpiScaleFactorRoundingPolicy)
enum class DpiAdjustmentPolicy { enum class DpiAdjustmentPolicy {
Unset, Unset,
Enabled, Enabled,