From 7793d47be92a1d04e3115a1d9a8384ad1e28e1db Mon Sep 17 00:00:00 2001 From: zccrs Date: Tue, 2 Apr 2019 15:00:08 +0800 Subject: [PATCH] feat: screen scale support takes effect in real time Change-Id: Ib1441c474af56cf8641f15a20d1069e419539ef7 --- platformthemeplugin/dthemesettings.cpp | 122 ++++++++++++++++--- platformthemeplugin/dthemesettings.h | 15 ++- platformthemeplugin/qdeepintheme.cpp | 159 +++++++++++++++++++++++++ platformthemeplugin/qdeepintheme.h | 1 + 4 files changed, 281 insertions(+), 16 deletions(-) diff --git a/platformthemeplugin/dthemesettings.cpp b/platformthemeplugin/dthemesettings.cpp index fba12116..f73b96ea 100644 --- a/platformthemeplugin/dthemesettings.cpp +++ b/platformthemeplugin/dthemesettings.cpp @@ -35,21 +35,22 @@ // 只能在时间上控制让两个冲突的逻辑错开。此处定义此时间(单位:ms),表示从touch begin到touch move比较在 // 此时间段内完成,否则不应该认为这个一个触摸滚动操作 #define TOUCH_FLICK_BEGIN_MOVE_DELAY QStringLiteral("TouchFlickBeginMoveDelay") +#define SCREEN_SCALE_FACTORS QStringLiteral("ScreenScaleFactors") +#define SCALE_FACTOR QStringLiteral("ScaleFactor") +#define SCALE_LOGICAL_DPI QStringLiteral("ScaleLogicalDpi") DCORE_USE_NAMESPACE -DThemeSettings::DThemeSettings(QObject *parent) +DThemeSettings::DThemeSettings(bool watchFile, QObject *parent) : QObject(parent) - , settings(QSettings::IniFormat, - QSettings::UserScope, - "deepin", "qt-theme") + , settings(makeSettings()) { - settings.setIniCodec("utf-8"); - settings.beginGroup("Theme"); + if (!watchFile) + return; QStringList list; - list << settings.fileName(); + list << settings->fileName(); list << QSettings(QSettings::IniFormat, QSettings::SystemScope, "deepin", "qt-theme").fileName(); @@ -71,14 +72,64 @@ DThemeSettings::DThemeSettings(QObject *parent) connect(watcher, &DFileWatcherManager::fileModified, this, &DThemeSettings::onConfigChanged); } +QSettings * DThemeSettings::makeSettings() +{ + QString saveConfigPath; + + do { + // 需要自定义读取主题相关配置的存储路径,未定义时默认为 ~/.config + // 适用于这样的情况:一个使用pkexec使用root权限启动的UI应用,需要跟随启动时的普通 + // 用户的字体、缩放等设置,可通过在QCoreApplication构造之前设置此环境变量指定使用 + // 某用户的主题配置文件。 + static QByteArray theme_config_path = qgetenv("D_QT_THEME_CONFIG_PATH"); + + if (theme_config_path.isEmpty()) { + break; + } + + // 先创建一个对象,用于获取默认配置文件的路径 + QSettings s(QSettings::IniFormat, QSettings::UserScope, "deepin", "qt-theme"); + const QString suffix("/deepin/qt-theme.ini"); + QString file_path = s.fileName(); + + // 必须以此路径结尾,去除此路径的剩余部分为配置文件路径 + if (!file_path.endsWith(suffix)) { + break; + } + + saveConfigPath = file_path.left(file_path.size() - suffix.size()); + + if (saveConfigPath.isEmpty()) { + break; + } + + // 设置自定义的主题配置文件存储目录 + QSettings::setPath(s.format(), s.scope(), QString::fromLocal8Bit(theme_config_path)); + } while (false); + + QSettings *s = new QSettings(QSettings::IniFormat, + QSettings::UserScope, + "deepin", "qt-theme"); + + // 恢复原本的配置目录 + if (!saveConfigPath.isEmpty()) { + QSettings::setPath(s->format(), s->scope(), saveConfigPath); + } + + s->setIniCodec("utf-8"); + s->beginGroup("Theme"); + + return s; +} + bool DThemeSettings::contains(const QString &key) const { - return settings.contains(key); + return settings->contains(key); } QVariant DThemeSettings::value(const QString &key, const QVariant &defaultValue) const { - return settings.value(key, defaultValue); + return settings->value(key, defaultValue); } bool DThemeSettings::isSetIconThemeName() const @@ -146,19 +197,56 @@ int DThemeSettings::touchFlickBeginMoveDelay() const return value(TOUCH_FLICK_BEGIN_MOVE_DELAY, 300).toInt(); } +qreal DThemeSettings::scaleFactor() const +{ + return value(SCALE_FACTOR).toReal(); +} + +QByteArray DThemeSettings::screenScaleFactors() const +{ + return value(SCREEN_SCALE_FACTORS).toByteArray(); +} + +// 从配置文件中获取dpi相关数据,文件中存储的格式为 ScaleLogicalDpi=x,y +// 会被QSettings解析为QStringList,此处需要将其转换为QPair +static QPair takePair(const QVariant &value) +{ + if (!value.isValid()) { + return qMakePair(0.0, 0.0); + } + + const QStringList &l = value.toStringList(); + + if (l.count() < 2) { + return qMakePair(0.0, 0.0); + } + + QPair ret; + + ret.first = l.first().toDouble(); + ret.second = l.at(1).toDouble(); + + return ret; +} + +QPair DThemeSettings::scaleLogicalDpi() const +{ + return takePair(value(SCALE_LOGICAL_DPI)); +} + void DThemeSettings::onConfigChanged() { QVariantMap config; - for (const QString &v : settings.allKeys()) { - config[v] = settings.value(v); + for (const QString &v : settings->allKeys()) { + config[v] = settings->value(v); } - settings.sync(); + settings->sync(); - for (const QString &v : settings.allKeys()) { + for (const QString &v : settings->allKeys()) { const QVariant &old_value = config.value(v); - const QVariant &new_value = settings.value(v); + const QVariant &new_value = settings->value(v); if (old_value != new_value) { if (v == ICON_THEME_NAME) @@ -175,6 +263,12 @@ void DThemeSettings::onConfigChanged() emit systemFontPointSizeChanged(new_value.toInt()); else if (v == TOUCH_FLICK_BEGIN_MOVE_DELAY) emit touchFlickBeginMoveDelayChanged(new_value.toInt()); + else if (v == SCREEN_SCALE_FACTORS) + emit screenScaleFactorsChanged(new_value.toByteArray()); + else if (v == SCALE_FACTOR) + emit scaleFactorChanged(new_value.toReal()); + else if (v == SCALE_LOGICAL_DPI) + emit scaleLogicalDpiChanged(takePair(new_value)); emit valueChanged(v, old_value, new_value); } diff --git a/platformthemeplugin/dthemesettings.h b/platformthemeplugin/dthemesettings.h index 181afc3f..bbeab018 100644 --- a/platformthemeplugin/dthemesettings.h +++ b/platformthemeplugin/dthemesettings.h @@ -35,9 +35,14 @@ class DThemeSettings : public QObject Q_PROPERTY(qreal systemFontPointSize READ systemFontPointSize NOTIFY systemFontPointSizeChanged) Q_PROPERTY(QStringList styleNames READ styleNames NOTIFY styleNamesChanged) Q_PROPERTY(int touchFlickBeginMoveDelay READ touchFlickBeginMoveDelay NOTIFY touchFlickBeginMoveDelayChanged) + Q_PROPERTY(qreal scaleFactor READ scaleFactor NOTIFY scaleFactorChanged) + Q_PROPERTY(QByteArray screenScaleFactors READ screenScaleFactors NOTIFY screenScaleFactorsChanged) + Q_PROPERTY(QPair scaleLogicalDpi READ scaleLogicalDpi NOTIFY scaleLogicalDpiChanged) public: - explicit DThemeSettings(QObject *parent = 0); + explicit DThemeSettings(bool watchFile = true, QObject *parent = 0); + + static QSettings *makeSettings(); bool contains(const QString &key) const; QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; @@ -55,6 +60,9 @@ class DThemeSettings : public QObject bool isSetSystemFixedFont() const; QString systemFixedFont() const; int touchFlickBeginMoveDelay() const; + qreal scaleFactor() const; + QByteArray screenScaleFactors() const; + QPair scaleLogicalDpi() const; signals: void valueChanged(const QString &key, const QVariant &oldValue, const QVariant &newValue); @@ -65,9 +73,12 @@ class DThemeSettings : public QObject void systemFixedFontChanged(QString systemFixedFont); void systemFontPointSizeChanged(qreal systemFontPointSize); void touchFlickBeginMoveDelayChanged(int touchFlickBeginMoveDelay); + void scaleFactorChanged(const qreal &scaleFactor); + void screenScaleFactorsChanged(const QByteArray &screenScaleFactors); + void scaleLogicalDpiChanged(const QPair scaleLogicalDpi); private: - QSettings settings; + QSettings *settings; void onConfigChanged(); }; diff --git a/platformthemeplugin/qdeepintheme.cpp b/platformthemeplugin/qdeepintheme.cpp index 836b3f75..6763d782 100644 --- a/platformthemeplugin/qdeepintheme.cpp +++ b/platformthemeplugin/qdeepintheme.cpp @@ -37,6 +37,13 @@ #include #include +#define private public +#include +#undef private +#include +#include +#include +#include #undef signals #include @@ -273,6 +280,134 @@ static void onIconThemeSetCallback() } } +static void updateAllWindowGeometry() +{ + for (QWindow *w : qGuiApp->allWindows()) { + if (w->type() == Qt::ForeignWindow || w->type() == Qt::Desktop) { + continue; + } + + // 通知窗口大小发送改变 + if (w->handle()) { + QWindowSystemInterfacePrivate::GeometryChangeEvent gce(w, QHighDpi::fromNativePixels(w->handle()->geometry(), w)); + QGuiApplicationPrivate::processGeometryChangeEvent(&gce); + } + } +} + +// 延迟一段时间更新窗口geometry,防止屏幕缩放比在短时间内连续变化时导致窗口闪动 +static void updateAllWindowGeometryDelay(int interval = 500) +{ + static QTimer *t = new QTimer(); + + t->setSingleShot(true); + t->setInterval(interval); + t->connect(t, &QTimer::timeout, t, updateAllWindowGeometry, Qt::UniqueConnection); + t->start(); +} + +static void notifyScreenScaleUpdated() +{ + for (QScreen *s : qGuiApp->screens()) { + Q_EMIT s->geometryChanged(s->geometry()); + + // 发射信号通知屏幕缩放比发生变化,DApplication中会定义此信号 + if (qGuiApp->metaObject()->indexOfSignal("screenDevicePixelRatioChanged(QScreen*)")) { + qGuiApp->metaObject()->invokeMethod(qGuiApp, "screenDevicePixelRatioChanged", Q_ARG(QScreen*, s)); + } + } +} + +static bool updateScaleFactor(qreal value) +{ + if (qIsNull(value)) { + value = 1.0; + } + + if (qFuzzyCompare(QHighDpiScaling::m_factor, value)) { + return false; + } + + QHighDpiScaling::setGlobalFactor(value); + + return true; +} + +static void onScaleFactorChanged(qreal value) +{ + if (updateScaleFactor(value)) { + notifyScreenScaleUpdated(); + updateAllWindowGeometryDelay(); + } +} + +static bool updateScaleLogcailDpi(const QPair &dpi) +{ + bool ok = false; + + if (!qIsNull(dpi.first)) { + QHighDpiScaling::m_logicalDpi.first = dpi.first; + ok = true; + } + + if (!qIsNull(dpi.second)) { + QHighDpiScaling::m_logicalDpi.second = dpi.second; + ok = true; + } + + return ok; +} + +static bool updateScreenScaleFactors(DThemeSettings *s, const QByteArray &value, bool unsetenv = false) +{ + if (qgetenv("QT_SCREEN_SCALE_FACTORS") == value) + return false; + + if (value.isEmpty()) { + if (!unsetenv) + return false; + + qunsetenv("QT_SCREEN_SCALE_FACTORS"); + } else { + qputenv("QT_SCREEN_SCALE_FACTORS", value); + } + + QHighDpiScaling::updateHighDpiScaling(); + + if (!updateScaleLogcailDpi(s->scaleLogicalDpi())) { + // 使用 QT_SCREEN_SCALE_FACTORS 为每个屏幕设置不同的缩放比之后,Qt会自动将 dpi 除以主屏的 + // 缩放倍数,以此来避免字体被放大。font dpi会影响未设置pixel size的QFont,默认情况下, + // QGuiApplication::font() 不会设置pixel size,因此,使用分屏幕设置不同缩放比后,字体却还 + // 是缩放前的大小。 + // 此处,如果设置了 ScreenScaleFactors,但未指定 ScaleLogcailDpi 时,默认将其重设回主屏 + // 的 logicalDpi。 + QHighDpiScaling::m_logicalDpi = qGuiApp->primaryScreen()->handle()->logicalDpi(); + } + + return true; +} +static void onScreenScaleFactorsChanged(const QByteArray &value) +{ + if (updateScreenScaleFactors(QDeepinTheme::getSettings(), value, true)) { + notifyScreenScaleUpdated(); + updateAllWindowGeometryDelay(); + } +} + +static bool enabledRTScreenScale() +{ + // 应用中设置了和屏幕缩放相关的环境变量或启动相关属性后后不开启自动缩放功能 + static bool enable = !qEnvironmentVariableIsSet("D_DISABLE_RT_SCREEN_SCALE") && + !qEnvironmentVariableIsSet("QT_DEVICE_PIXEL_RATIO") && + !qEnvironmentVariableIsSet("QT_SCALE_FACTOR") && + !qEnvironmentVariableIsSet("QT_AUTO_SCREEN_SCALE_FACTOR") && + !qEnvironmentVariableIsSet("QT_SCREEN_SCALE_FACTORS") && + !QCoreApplication::testAttribute(Qt::AA_DisableHighDpiScaling) && + !QCoreApplication::testAttribute(Qt::AA_EnableHighDpiScaling); + + return enable; +} + QDeepinTheme::QDeepinTheme() { #if XDG_ICON_VERSION_MAR >= 3 @@ -280,6 +415,16 @@ QDeepinTheme::QDeepinTheme() DEEPIN_QT_THEME::setFollowColorScheme = XdgIcon::setFollowColorScheme; DEEPIN_QT_THEME::followColorScheme = XdgIcon::followColorScheme; #endif + + if (enabledRTScreenScale()) { + QScopedPointer setting(new DThemeSettings(false)); + // 程序启动时初始设置屏幕缩放比 + updateScaleFactor(setting->scaleFactor()); + + if (!updateScreenScaleFactors(setting.data(), setting->screenScaleFactors())) { + updateScaleLogcailDpi(setting->scaleLogicalDpi()); + } + } } QDeepinTheme::~QDeepinTheme() @@ -447,9 +592,23 @@ DThemeSettings *QDeepinTheme::settings() const QObject::connect(m_settings, &DThemeSettings::systemFontChanged, m_settings, updateSystemFont, Qt::UniqueConnection); QObject::connect(m_settings, &DThemeSettings::systemFontPointSizeChanged, m_settings, updateSystemFont, Qt::UniqueConnection); QObject::connect(m_settings, &DThemeSettings::iconThemeNameChanged, m_settings, &onIconThemeSetCallback, Qt::UniqueConnection); + + if (enabledRTScreenScale()) { + QObject::connect(m_settings, &DThemeSettings::scaleFactorChanged, + m_settings, onScaleFactorChanged, Qt::UniqueConnection); + QObject::connect(m_settings, &DThemeSettings::screenScaleFactorsChanged, + m_settings, onScreenScaleFactorsChanged, Qt::UniqueConnection); + QObject::connect(m_settings, &DThemeSettings::scaleLogicalDpiChanged, + m_settings, updateScaleLogcailDpi, Qt::UniqueConnection); + } } return m_settings; } +DThemeSettings *QDeepinTheme::getSettings() +{ + return m_settings; +} + QT_END_NAMESPACE diff --git a/platformthemeplugin/qdeepintheme.h b/platformthemeplugin/qdeepintheme.h index cf87fa21..76613c0a 100644 --- a/platformthemeplugin/qdeepintheme.h +++ b/platformthemeplugin/qdeepintheme.h @@ -47,6 +47,7 @@ class QDeepinTheme : public QGenericUnixTheme QVariant themeHint(ThemeHint hint) const Q_DECL_OVERRIDE; const QFont *font(Font type) const Q_DECL_OVERRIDE; DThemeSettings *settings() const; + static DThemeSettings *getSettings(); static const char *name;