File indexing completed on 2024-05-12 15:42:37
0001 /* 0002 * SPDX-FileCopyrightText: 2017 by Marco Martin <mart@kde.org> 0003 * SPDX-FileCopyrightText: 2021 Arjen Hiemstra <ahiemstra@heimr.nl> 0004 * 0005 * SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "basictheme_p.h" 0009 0010 #include "styleselector_p.h" 0011 #include <QFile> 0012 #include <QGuiApplication> 0013 0014 #include "loggingcategory.h" 0015 0016 namespace Kirigami 0017 { 0018 class CompatibilityThemeDefinition : public BasicThemeDefinition 0019 { 0020 Q_OBJECT 0021 public: 0022 CompatibilityThemeDefinition(QObject *component, QObject *parent = nullptr) 0023 : BasicThemeDefinition(parent) 0024 { 0025 m_object = component; 0026 0027 connect(m_object, SIGNAL(textColorChanged()), this, SLOT(syncFromQml())); 0028 connect(m_object, SIGNAL(disabledTextColorChanged()), this, SLOT(syncFromQml())); 0029 connect(m_object, SIGNAL(highlightColorChanged()), this, SLOT(syncFromQml())); 0030 connect(m_object, SIGNAL(highlightedTextColorChanged()), this, SLOT(syncFromQml())); 0031 connect(m_object, SIGNAL(backgroundColorChanged()), this, SLOT(syncFromQml())); 0032 connect(m_object, SIGNAL(alternateBackgroundColorChanged()), this, SLOT(syncFromQml())); 0033 connect(m_object, SIGNAL(linkColorChanged()), this, SLOT(syncFromQml())); 0034 connect(m_object, SIGNAL(visitedLinkColorChanged()), this, SLOT(syncFromQml())); 0035 connect(m_object, SIGNAL(buttonTextColorChanged()), this, SLOT(syncFromQml())); 0036 connect(m_object, SIGNAL(buttonBackgroundColorChanged()), this, SLOT(syncFromQml())); 0037 connect(m_object, SIGNAL(buttonAlternateBackgroundColorChanged()), this, SLOT(syncFromQml())); 0038 connect(m_object, SIGNAL(buttonHoverColorChanged()), this, SLOT(syncFromQml())); 0039 connect(m_object, SIGNAL(buttonFocusColorChanged()), this, SLOT(syncFromQml())); 0040 connect(m_object, SIGNAL(viewTextColorChanged()), this, SLOT(syncFromQml())); 0041 connect(m_object, SIGNAL(viewBackgroundColorChanged()), this, SLOT(syncFromQml())); 0042 connect(m_object, SIGNAL(viewAlternateBackgroundColorChanged()), this, SLOT(syncFromQml())); 0043 connect(m_object, SIGNAL(viewHoverColorChanged()), this, SLOT(syncFromQml())); 0044 connect(m_object, SIGNAL(viewFocusColorChanged()), this, SLOT(syncFromQml())); 0045 connect(m_object, SIGNAL(complementaryTextColorChanged()), this, SLOT(syncFromQml())); 0046 connect(m_object, SIGNAL(complementaryBackgroundColorChanged()), this, SLOT(syncFromQml())); 0047 connect(m_object, SIGNAL(complementaryAlternateBackgroundColorChanged()), this, SLOT(syncFromQml())); 0048 connect(m_object, SIGNAL(complementaryHoverColorChanged()), this, SLOT(syncFromQml())); 0049 connect(m_object, SIGNAL(complementaryFocusColorChanged()), this, SLOT(syncFromQml())); 0050 } 0051 0052 void syncToQml(PlatformTheme *object) override 0053 { 0054 BasicThemeDefinition::syncToQml(object); 0055 0056 QMetaObject::invokeMethod(m_object, "__propagateColorSet", Q_ARG(QVariant, QVariant::fromValue(object->parent())), Q_ARG(QVariant, object->colorSet())); 0057 QMetaObject::invokeMethod(m_object, 0058 "__propagateTextColor", 0059 Q_ARG(QVariant, QVariant::fromValue(object->parent())), 0060 Q_ARG(QVariant, object->textColor())); 0061 QMetaObject::invokeMethod(m_object, 0062 "__propagateBackgroundColor", 0063 Q_ARG(QVariant, QVariant::fromValue(object->parent())), 0064 Q_ARG(QVariant, object->backgroundColor())); 0065 QMetaObject::invokeMethod(m_object, 0066 "__propagatePrimaryColor", 0067 Q_ARG(QVariant, QVariant::fromValue(object->parent())), 0068 Q_ARG(QVariant, object->highlightColor())); 0069 QMetaObject::invokeMethod(m_object, 0070 "__propagateAccentColor", 0071 Q_ARG(QVariant, QVariant::fromValue(object->parent())), 0072 Q_ARG(QVariant, object->highlightColor())); 0073 } 0074 0075 Q_SLOT void syncFromQml() 0076 { 0077 textColor = m_object->property("textColor").value<QColor>(); 0078 disabledTextColor = m_object->property("disabledTextColor").value<QColor>(); 0079 highlightColor = m_object->property("highlightColor").value<QColor>(); 0080 highlightedTextColor = m_object->property("highlightedTextColor").value<QColor>(); 0081 backgroundColor = m_object->property("backgroundColor").value<QColor>(); 0082 alternateBackgroundColor = m_object->property("alternateBackgroundColor").value<QColor>(); 0083 linkColor = m_object->property("linkColor").value<QColor>(); 0084 visitedLinkColor = m_object->property("visitedLinkColor").value<QColor>(); 0085 buttonTextColor = m_object->property("buttonTextColor").value<QColor>(); 0086 buttonBackgroundColor = m_object->property("buttonBackgroundColor").value<QColor>(); 0087 buttonAlternateBackgroundColor = m_object->property("buttonAlternateBackgroundColor").value<QColor>(); 0088 buttonHoverColor = m_object->property("buttonHoverColor").value<QColor>(); 0089 buttonFocusColor = m_object->property("buttonFocusColor").value<QColor>(); 0090 viewTextColor = m_object->property("viewTextColor").value<QColor>(); 0091 viewBackgroundColor = m_object->property("viewBackgroundColor").value<QColor>(); 0092 viewAlternateBackgroundColor = m_object->property("viewAlternateBackgroundColor").value<QColor>(); 0093 viewHoverColor = m_object->property("viewHoverColor").value<QColor>(); 0094 viewFocusColor = m_object->property("viewFocusColor").value<QColor>(); 0095 complementaryTextColor = m_object->property("complementaryTextColor").value<QColor>(); 0096 complementaryBackgroundColor = m_object->property("complementaryBackgroundColor").value<QColor>(); 0097 complementaryAlternateBackgroundColor = m_object->property("complementaryAlternateBackgroundColor").value<QColor>(); 0098 complementaryHoverColor = m_object->property("complementaryHoverColor").value<QColor>(); 0099 complementaryFocusColor = m_object->property("complementaryFocusColor").value<QColor>(); 0100 0101 Q_EMIT changed(); 0102 } 0103 0104 private: 0105 QObject *m_object; 0106 }; 0107 0108 BasicThemeDefinition::BasicThemeDefinition(QObject *parent) 0109 : QObject(parent) 0110 { 0111 defaultFont = qGuiApp->font(); 0112 0113 smallFont = qGuiApp->font(); 0114 smallFont.setPointSize(smallFont.pointSize() - 2); 0115 } 0116 0117 void BasicThemeDefinition::syncToQml(PlatformTheme *object) 0118 { 0119 auto item = qobject_cast<QQuickItem *>(object->parent()); 0120 if (item && qmlAttachedPropertiesObject<PlatformTheme>(item, false) == object) { 0121 Q_EMIT sync(item); 0122 } 0123 } 0124 0125 BasicThemeInstance::BasicThemeInstance(QObject *parent) 0126 : QObject(parent) 0127 { 0128 } 0129 0130 BasicThemeDefinition &BasicThemeInstance::themeDefinition(QQmlEngine *engine) 0131 { 0132 if (m_themeDefinition) { 0133 return *m_themeDefinition; 0134 } 0135 0136 auto componentUrl = StyleSelector::componentUrl(QStringLiteral("Theme.qml")); 0137 QString path{componentUrl.toLocalFile()}; 0138 if (path.isEmpty() && componentUrl.scheme() == QLatin1String("qrc")) { 0139 path = QLatin1Char(':') + componentUrl.path(); 0140 } 0141 QFile themeFile{path}; 0142 if (themeFile.open(QIODevice::ReadOnly)) { 0143 auto data = themeFile.readAll(); 0144 0145 // Before Kirigami 5.80, custom Theme files would be registered as a 0146 // "Theme" singleton that would then be proxied by BasicTheme. This has 0147 // changed with the Theme singleton being an instance of BasicTheme. 0148 // However, this means that old theme files fail to load because 0149 // QQmlComponent complains about "pragma Singleton". To workaround this, 0150 // we remove the pragma here, as everything else should still work 0151 // correctly. 0152 // TODO KF6: Remove this and rely on all Theme files not being singletons. 0153 data.replace("\npragma Singleton\n", ""); 0154 0155 QQmlComponent component(engine); 0156 component.setData(data, componentUrl); 0157 auto result = component.create(); 0158 0159 if (!result) { 0160 const auto errors = component.errors(); 0161 for (auto error : errors) { 0162 qCWarning(KirigamiLog) << error.toString(); 0163 } 0164 0165 qCWarning(KirigamiLog) << "Invalid Theme file, using default Basic theme."; 0166 m_themeDefinition = std::make_unique<BasicThemeDefinition>(); 0167 } else if (qobject_cast<BasicThemeDefinition *>(result)) { 0168 m_themeDefinition.reset(qobject_cast<BasicThemeDefinition *>(result)); 0169 } else { 0170 qCWarning(KirigamiLog) << "Warning: Theme implementations should use Kirigami.BasicThemeDefinition for its root item"; 0171 m_themeDefinition = std::make_unique<CompatibilityThemeDefinition>(result); 0172 } 0173 } else { 0174 qCDebug(KirigamiLog) << "No Theme file found, using default Basic theme"; 0175 m_themeDefinition = std::make_unique<BasicThemeDefinition>(); 0176 } 0177 0178 connect(m_themeDefinition.get(), &BasicThemeDefinition::changed, this, &BasicThemeInstance::onDefinitionChanged); 0179 0180 return *m_themeDefinition; 0181 } 0182 0183 void BasicThemeInstance::onDefinitionChanged() 0184 { 0185 for (auto watcher : std::as_const(watchers)) { 0186 watcher->sync(); 0187 } 0188 } 0189 0190 Q_GLOBAL_STATIC(BasicThemeInstance, basicThemeInstance) 0191 0192 BasicTheme::BasicTheme(QObject *parent) 0193 : PlatformTheme(parent) 0194 { 0195 basicThemeInstance()->watchers.append(this); 0196 0197 sync(); 0198 } 0199 0200 BasicTheme::~BasicTheme() 0201 { 0202 basicThemeInstance()->watchers.removeOne(this); 0203 } 0204 0205 void BasicTheme::sync() 0206 { 0207 auto &definition = basicThemeInstance()->themeDefinition(qmlEngine(parent())); 0208 0209 switch (colorSet()) { 0210 case BasicTheme::Button: 0211 setTextColor(tint(definition.buttonTextColor)); 0212 setBackgroundColor(tint(definition.buttonBackgroundColor)); 0213 setAlternateBackgroundColor(tint(definition.buttonAlternateBackgroundColor)); 0214 setHoverColor(tint(definition.buttonHoverColor)); 0215 setFocusColor(tint(definition.buttonFocusColor)); 0216 break; 0217 case BasicTheme::View: 0218 setTextColor(tint(definition.viewTextColor)); 0219 setBackgroundColor(tint(definition.viewBackgroundColor)); 0220 setAlternateBackgroundColor(tint(definition.viewAlternateBackgroundColor)); 0221 setHoverColor(tint(definition.viewHoverColor)); 0222 setFocusColor(tint(definition.viewFocusColor)); 0223 break; 0224 case BasicTheme::Selection: 0225 setTextColor(tint(definition.selectionTextColor)); 0226 setBackgroundColor(tint(definition.selectionBackgroundColor)); 0227 setAlternateBackgroundColor(tint(definition.selectionAlternateBackgroundColor)); 0228 setHoverColor(tint(definition.selectionHoverColor)); 0229 setFocusColor(tint(definition.selectionFocusColor)); 0230 break; 0231 case BasicTheme::Tooltip: 0232 setTextColor(tint(definition.tooltipTextColor)); 0233 setBackgroundColor(tint(definition.tooltipBackgroundColor)); 0234 setAlternateBackgroundColor(tint(definition.tooltipAlternateBackgroundColor)); 0235 setHoverColor(tint(definition.tooltipHoverColor)); 0236 setFocusColor(tint(definition.tooltipFocusColor)); 0237 break; 0238 case BasicTheme::Complementary: 0239 setTextColor(tint(definition.complementaryTextColor)); 0240 setBackgroundColor(tint(definition.complementaryBackgroundColor)); 0241 setAlternateBackgroundColor(tint(definition.complementaryAlternateBackgroundColor)); 0242 setHoverColor(tint(definition.complementaryHoverColor)); 0243 setFocusColor(tint(definition.complementaryFocusColor)); 0244 break; 0245 case BasicTheme::Window: 0246 default: 0247 setTextColor(tint(definition.textColor)); 0248 setBackgroundColor(tint(definition.backgroundColor)); 0249 setAlternateBackgroundColor(tint(definition.alternateBackgroundColor)); 0250 setHoverColor(tint(definition.hoverColor)); 0251 setFocusColor(tint(definition.focusColor)); 0252 break; 0253 } 0254 0255 setDisabledTextColor(tint(definition.disabledTextColor)); 0256 setHighlightColor(tint(definition.highlightColor)); 0257 setHighlightedTextColor(tint(definition.highlightedTextColor)); 0258 setActiveTextColor(tint(definition.activeTextColor)); 0259 setActiveBackgroundColor(tint(definition.activeBackgroundColor)); 0260 setLinkColor(tint(definition.linkColor)); 0261 setLinkBackgroundColor(tint(definition.linkBackgroundColor)); 0262 setVisitedLinkColor(tint(definition.visitedLinkColor)); 0263 setVisitedLinkBackgroundColor(tint(definition.visitedLinkBackgroundColor)); 0264 setNegativeTextColor(tint(definition.negativeTextColor)); 0265 setNegativeBackgroundColor(tint(definition.negativeBackgroundColor)); 0266 setNeutralTextColor(tint(definition.neutralTextColor)); 0267 setNeutralBackgroundColor(tint(definition.neutralBackgroundColor)); 0268 setPositiveTextColor(tint(definition.positiveTextColor)); 0269 setPositiveBackgroundColor(tint(definition.positiveBackgroundColor)); 0270 0271 setDefaultFont(definition.defaultFont); 0272 setSmallFont(definition.smallFont); 0273 } 0274 0275 bool BasicTheme::event(QEvent *event) 0276 { 0277 if (event->type() == PlatformThemeEvents::DataChangedEvent::type) { 0278 sync(); 0279 } 0280 0281 if (event->type() == PlatformThemeEvents::ColorSetChangedEvent::type) { 0282 sync(); 0283 } 0284 0285 if (event->type() == PlatformThemeEvents::ColorGroupChangedEvent::type) { 0286 sync(); 0287 } 0288 0289 if (event->type() == PlatformThemeEvents::ColorChangedEvent::type) { 0290 basicThemeInstance()->themeDefinition(qmlEngine(parent())).syncToQml(this); 0291 } 0292 0293 if (event->type() == PlatformThemeEvents::FontChangedEvent::type) { 0294 basicThemeInstance()->themeDefinition(qmlEngine(parent())).syncToQml(this); 0295 } 0296 0297 return PlatformTheme::event(event); 0298 } 0299 0300 QColor BasicTheme::tint(const QColor &color) 0301 { 0302 switch (colorGroup()) { 0303 case PlatformTheme::Inactive: 0304 return QColor::fromHsvF(color.hueF(), color.saturationF() * 0.5, color.valueF()); 0305 case PlatformTheme::Disabled: 0306 return QColor::fromHsvF(color.hueF(), color.saturationF() * 0.5, color.valueF() * 0.8); 0307 default: 0308 return color; 0309 } 0310 } 0311 0312 } 0313 0314 #include "basictheme.moc" 0315 #include "moc_basictheme_p.cpp"