diff --git a/src/declarativeimports/core/units.cpp b/src/declarativeimports/core/units.cpp index a9d6ddc16..146ce8525 100644 --- a/src/declarativeimports/core/units.cpp +++ b/src/declarativeimports/core/units.cpp @@ -1,283 +1,279 @@ /*************************************************************************** * Copyright 2013 Marco Martin * * Copyright 2014 Sebastian Kügler * * Copyright 2014 David Edmundson * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "units.h" #include #include #include #include #include #include #include #include -#include +#include #include -QString plasmarc() { return QStringLiteral("plasmarc"); } -QString groupName() { return QStringLiteral("Units"); } const int defaultLongDuration = 120; SharedAppFilter::SharedAppFilter(QObject *parent) : QObject(parent) { QCoreApplication::instance()->installEventFilter(this); } SharedAppFilter::~SharedAppFilter() {} bool SharedAppFilter::eventFilter(QObject *watched, QEvent *event) { if (watched == QCoreApplication::instance()) { if (event->type() == QEvent::ApplicationFontChange) { emit fontChanged(); } } return QObject::eventFilter(watched, event); } SharedAppFilter *Units::s_sharedAppFilter = nullptr; Units::Units(QObject *parent) : QObject(parent), m_gridUnit(-1), m_devicePixelRatio(-1), m_smallSpacing(-1), m_largeSpacing(-1), m_longDuration(defaultLongDuration) // default base value for animations { if (!s_sharedAppFilter) { s_sharedAppFilter = new SharedAppFilter(); } m_iconSizes = new QQmlPropertyMap(this); m_iconSizeHints = new QQmlPropertyMap(this); updateDevicePixelRatio(); // also updates icon sizes updateSpacing(); // updates gridUnit and *Spacing properties connect(KIconLoader::global(), &KIconLoader::iconLoaderSettingsChanged, this, &Units::iconLoaderSettingsChanged); QObject::connect(s_sharedAppFilter, &SharedAppFilter::fontChanged, this, &Units::updateSpacing); - const QString configFile = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1Char('/') + plasmarc(); - KDirWatch::self()->addFile(configFile); - - // Catch both, direct changes to the config file ... - connect(KDirWatch::self(), &KDirWatch::dirty, this, &Units::settingsFileChanged); - // ... but also remove/recreate cycles, like KConfig does it - connect(KDirWatch::self(), &KDirWatch::created, this, &Units::settingsFileChanged); - // read configuration - updatePlasmaRCSettings(); + m_animationSpeedWatcher = KConfigWatcher::create(KSharedConfig::openConfig()); + connect(m_animationSpeedWatcher.data(), &KConfigWatcher::configChanged, this, + [this](const KConfigGroup &group, const QByteArrayList &names) { + if (group.name() == QLatin1String("KDE") && names.contains(QByteArrayLiteral("AnimationDurationFactor"))) { + updateAnimationSpeed(); + } + }); + updateAnimationSpeed(); } Units::~Units() { } Units &Units::instance() { static Units units; return units; } -void Units::settingsFileChanged(const QString &file) +void Units::updateAnimationSpeed() { - if (file.endsWith(plasmarc())) { - KSharedConfigPtr cfg = KSharedConfig::openConfig(plasmarc()); - cfg->reparseConfiguration(); - updatePlasmaRCSettings(); - } -} + KConfigGroup generalCfg = KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("KDE")); + const qreal animationSpeedModifier = qMax(0.0, generalCfg.readEntry("AnimationDurationFactor", 1.0)); + + // Read the old longDuration value for compatibility + KConfigGroup cfg = KConfigGroup(KSharedConfig::openConfig(QStringLiteral("plasmarc")), QStringLiteral("Units")); + int longDuration = cfg.readEntry("longDuration", defaultLongDuration); + + longDuration = qRound(longDuration * animationSpeedModifier); -void Units::updatePlasmaRCSettings() -{ - KConfigGroup cfg = KConfigGroup(KSharedConfig::openConfig(plasmarc()), groupName()); // Animators with a duration of 0 do not fire reliably // see Bug 357532 and QTBUG-39766 - const int longDuration = qMax(1, cfg.readEntry("longDuration", defaultLongDuration)); + longDuration = qMax(1, longDuration); if (longDuration != m_longDuration) { m_longDuration = longDuration; emit durationChanged(); } } void Units::iconLoaderSettingsChanged() { m_iconSizes->insert(QStringLiteral("desktop"), devicePixelIconSize(KIconLoader::global()->currentSize(KIconLoader::Desktop))); m_iconSizes->insert(QStringLiteral("tiny"), devicePixelIconSize(KIconLoader::SizeSmall) / 2); m_iconSizes->insert(QStringLiteral("small"), devicePixelIconSize(KIconLoader::SizeSmall)); m_iconSizes->insert(QStringLiteral("smallMedium"), devicePixelIconSize(KIconLoader::SizeSmallMedium)); m_iconSizes->insert(QStringLiteral("medium"), devicePixelIconSize(KIconLoader::SizeMedium)); m_iconSizes->insert(QStringLiteral("large"), devicePixelIconSize(KIconLoader::SizeLarge)); m_iconSizes->insert(QStringLiteral("huge"), devicePixelIconSize(KIconLoader::SizeHuge)); m_iconSizes->insert(QStringLiteral("enormous"), devicePixelIconSize(KIconLoader::SizeEnormous)); m_iconSizeHints->insert(QStringLiteral("panel"), devicePixelIconSize(KIconLoader::global()->currentSize(KIconLoader::Panel))); m_iconSizeHints->insert(QStringLiteral("desktop"), devicePixelIconSize(KIconLoader::global()->currentSize(KIconLoader::Desktop))); emit iconSizesChanged(); emit iconSizeHintsChanged(); } QQmlPropertyMap *Units::iconSizes() const { return m_iconSizes; } QQmlPropertyMap *Units::iconSizeHints() const { return m_iconSizeHints; } int Units::roundToIconSize(int size) { /*Do *not* use devicePixelIconSize here, we want to use the sizes of the pixmaps of the smallest icons on the disk. And those are unaffected by dpi*/ if (size <= 0) { return 0; } else if (size < KIconLoader::SizeSmall) { return KIconLoader::SizeSmall/2; } else if (size < KIconLoader::SizeSmallMedium) { return KIconLoader::SizeSmall; } else if (size < KIconLoader::SizeMedium) { return KIconLoader::SizeSmallMedium; } else if (size < KIconLoader::SizeLarge) { return KIconLoader::SizeMedium; } else if (size < KIconLoader::SizeHuge) { return KIconLoader::SizeLarge; } else { return size; } } int Units::devicePixelIconSize(const int size) const { /* in kiconloader.h enum StdSizes { SizeSmall=16, SizeSmallMedium=22, SizeMedium=32, SizeLarge=48, SizeHuge=64, SizeEnormous=128 }; */ // Scale the icon sizes up using the devicePixelRatio // This function returns the next stepping icon size // and multiplies the global settings with the dpi ratio. const qreal ratio = devicePixelRatio(); if (ratio < 1.5) { return size; } else if (ratio < 2.0) { return size * 1.5; } else if (ratio < 2.5) { return size * 2.0; } else if (ratio < 3.0) { return size * 2.5; } else if (ratio < 3.5) { return size * 3.0; } else { return size * ratio; } // FIXME: Add special casing for < 64 cases: align to kiconloader size } qreal Units::devicePixelRatio() const { return m_devicePixelRatio; } void Units::updateDevicePixelRatio() { // Using QGuiApplication::devicePixelRatio() gives too coarse values, // i.e. it directly jumps from 1.0 to 2.0. We want tighter control on // sizing, so we compute the exact ratio and use that. // TODO: make it possible to adapt to the dpi for the current screen dpi // instead of assuming that all of them use the same dpi which applies for // X11 but not for other systems. QScreen *primary = QGuiApplication::primaryScreen(); if (!primary) { return; } const qreal dpi = primary->logicalDotsPerInchX(); // Usual "default" is 96 dpi // that magic ratio follows the definition of "device independent pixel" by Microsoft m_devicePixelRatio = (qreal)dpi / (qreal)96; iconLoaderSettingsChanged(); emit devicePixelRatioChanged(); } int Units::gridUnit() const { return m_gridUnit; } int Units::smallSpacing() const { return m_smallSpacing; } int Units::largeSpacing() const { return m_largeSpacing; } void Units::updateSpacing() { int gridUnit = QFontMetrics(QGuiApplication::font()).boundingRect(QStringLiteral("M")).height(); if (gridUnit % 2 != 0) { gridUnit++; } if (gridUnit != m_gridUnit) { m_gridUnit = gridUnit; emit gridUnitChanged(); } if (gridUnit != m_largeSpacing) { m_smallSpacing = qMax(2, (int)(gridUnit / 4)); // 1/4 of gridUnit, at least 2 m_largeSpacing = gridUnit; // msize.height emit spacingChanged(); } } int Units::longDuration() const { return m_longDuration; } int Units::shortDuration() const { return qMax(1, m_longDuration / 5); } #include "moc_units.cpp" diff --git a/src/declarativeimports/core/units.h b/src/declarativeimports/core/units.h index c373d566e..f3d444c7f 100644 --- a/src/declarativeimports/core/units.h +++ b/src/declarativeimports/core/units.h @@ -1,239 +1,241 @@ /*************************************************************************** * Copyright 2013 Marco Martin * * Copyright 2014 Sebastian Kügler * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #ifndef UNITS_H #define UNITS_H #include #include #include +#include + class QQuickItem; class SharedAppFilter : public QObject { Q_OBJECT public: explicit SharedAppFilter(QObject *parent = nullptr); ~SharedAppFilter() override; Q_SIGNALS: void fontChanged(); protected: bool eventFilter(QObject *watched, QEvent *event) override; }; /** * @class Units * @short Expose sizes to QML */ class Units : public QObject { Q_OBJECT /** * The fundamental unit of space that should be used for sizes, expressed in pixels. * Given the screen has an accurate DPI settings, it corresponds to a width of * the capital letter M */ Q_PROPERTY(int gridUnit READ gridUnit NOTIFY gridUnitChanged) /** * units.iconSizes provides access to platform-dependent icon sizing * * The icon sizes provided are normalized for different DPI, so icons * will scale depending on the DPI. * * Icon sizes from KIconLoader, adjusted to devicePixelRatio: * * small * * smallMedium * * medium * * large * * huge * * enormous * * desktop (DEPRECATED: use iconSizeHints instead) * */ //note the iconSizeChanged signal indicates that one (or more) of these icons have changed //but the property map itself remains constant Q_PROPERTY(QQmlPropertyMap *iconSizes READ iconSizes CONSTANT) /** * units.iconSizeHints provides access to user-configurable icon size hints, * to be used where appropriate in the user interface. * * Conceptually, an icon size hint is a key that has one of the sizes from * @iconSizes property as value. * * Currently available hints: * * panel * * desktop */ //note the iconSizeHintsChanged signal indicates that one (or more) of these icons have changed //but the property map itself remains constant Q_PROPERTY(QQmlPropertyMap *iconSizeHints READ iconSizeHints CONSTANT) // layout hints /** * units.smallSpacing is the amount of spacing that should be used around smaller UI elements, * for example as spacing in Columns. Internally, this size depends on the size of * the default font as rendered on the screen, so it takes user-configured font size and DPI * into account. */ Q_PROPERTY(int smallSpacing READ smallSpacing NOTIFY spacingChanged) /** * units.largeSpacing is the amount of spacing that should be used inside bigger UI elements, * for example between an icon and the corresponding text. Internally, this size depends on * the size of the default font as rendered on the screen, so it takes user-configured font * size and DPI into account. */ Q_PROPERTY(int largeSpacing READ largeSpacing NOTIFY spacingChanged) /** * The ratio between physical and device-independent pixels. This value does not depend on the \ * size of the configured font. If you want to take font sizes into account when scaling elements, * use theme.mSize(theme.defaultFont), units.smallSpacing and units.largeSpacing. * The devicePixelRatio follows the definition of "device independent pixel" by Microsoft. */ Q_PROPERTY(qreal devicePixelRatio READ devicePixelRatio NOTIFY devicePixelRatioChanged) /** * units.longDuration should be used for longer, screen-covering animations, for opening and * closing of dialogs and other "not too small" animations */ Q_PROPERTY(int longDuration READ longDuration NOTIFY durationChanged) /** * units.shortDuration should be used for short animations, such as accentuating a UI event, * hover events, etc.. */ Q_PROPERTY(int shortDuration READ shortDuration NOTIFY durationChanged) public: /// @cond INTERNAL_DOCS ~Units(); /** * @return a reference to the global Units instance * @since 5.31 */ static Units &instance(); /** * @return pixel value for a grid Unit. Depends on DPI and font size. */ int gridUnit() const; /** * @return The ratio between physical and device-independent pixels. */ qreal devicePixelRatio() const; /** * @return map with iconsizes, indexed by name */ QQmlPropertyMap *iconSizes() const; /** * @return map with user-configurable icon size hints, indexed by name * @since 5.33 */ QQmlPropertyMap *iconSizeHints() const; /** * @return Pixel value for large spacing between elements. * @since 5.0 */ int smallSpacing() const; /** * @return Pixel value for large spacing between elements. * @since 5.0 */ int largeSpacing() const; /** * @return Duration for long animations, in milliseconds. * @since 5.0 */ int longDuration() const; /** * @return Duration for short animations, in milliseconds. * @since 5.0 */ int shortDuration() const; /// @endcond /** * @return a size rounded tothe nearest inferior standard icon size. * sizes larger than iconSizes.huge, it will be returned unmodified * @param int size the size we want to be rounded down * @see iconSizes */ Q_INVOKABLE static int roundToIconSize(int size); Q_SIGNALS: void devicePixelRatioChanged(); void gridUnitChanged(); void iconSizesChanged(); void iconSizeHintsChanged(); void spacingChanged(); void durationChanged(); private Q_SLOTS: void iconLoaderSettingsChanged(); - void settingsFileChanged(const QString &file); void updateSpacing(); private: Units(QObject *parent = nullptr); Units(Units const&) = delete; // Copy construct Units(Units&&) = delete; // Move construct Units& operator=(Units const&) = delete; // Copy assign Units& operator=(Units &&) = delete; // Move assign void updateDevicePixelRatio(); - void updatePlasmaRCSettings(); + void updateAnimationSpeed(); /** * @return The dpi-adjusted size for a given icon size */ int devicePixelIconSize(const int size) const; int m_gridUnit; qreal m_devicePixelRatio; QQmlPropertyMap *m_iconSizes; QQmlPropertyMap *m_iconSizeHints; static SharedAppFilter *s_sharedAppFilter; int m_smallSpacing; int m_largeSpacing; + KConfigWatcher::Ptr m_animationSpeedWatcher; int m_longDuration; }; #endif //UNITS_H