File indexing completed on 2024-05-19 04:27:04
0001 /* 0002 * This file is part of the KDE project 0003 * SPDX-FileCopyrightText: 2014 Arjen Hiemstra <ahiemstra@heimr.nl> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "Theme.h" 0009 0010 #include <QStringList> 0011 #include <QUrl> 0012 #include <kis_debug.h> 0013 #include <QFile> 0014 #include <QDir> 0015 #include <QColor> 0016 #include <QFont> 0017 #include <QFontDatabase> 0018 #include <QApplication> 0019 #include <QWidget> 0020 #include <QQmlComponent> 0021 #include <QStandardPaths> 0022 #include <KoResourcePaths.h> 0023 0024 #include "QmlGlobalEngine.h" 0025 0026 class Q_DECL_HIDDEN Theme::Private 0027 { 0028 public: 0029 Private() 0030 : iconPath("icons/") 0031 , imagePath("images/") 0032 , fontPath("fonts/") 0033 , fontsAdded(false) 0034 , lineCountLandscape(40) 0035 , lineCountPortrait(70) 0036 { } 0037 0038 void loadFonts(); 0039 void rebuildFontCache(); 0040 0041 QString id; 0042 QString name; 0043 0044 QVariantMap colors; 0045 QVariantMap sizes; 0046 QVariantMap fonts; 0047 0048 QString basePath; 0049 QString iconPath; 0050 QString imagePath; 0051 QString fontPath; 0052 0053 QHash<QString, QColor> colorCache; 0054 QHash<QString, QFont> fontMap; 0055 0056 bool fontsAdded; 0057 QList<int> addedFonts; 0058 int lineCountLandscape; 0059 int lineCountPortrait; 0060 }; 0061 0062 Theme::Theme(QObject* parent) 0063 : QObject(parent), d(new Private) 0064 { 0065 qApp->installEventFilter(this); 0066 } 0067 0068 Theme::~Theme() 0069 { 0070 QFontDatabase db; 0071 Q_FOREACH(int id, d->addedFonts) { 0072 db.removeApplicationFont(id); 0073 } 0074 0075 delete d; 0076 } 0077 0078 QString Theme::id() const 0079 { 0080 return d->id; 0081 } 0082 0083 void Theme::setId(const QString& newValue) 0084 { 0085 if (newValue != d->id) { 0086 d->id = newValue; 0087 const QString themeQmlPath = themePath(d->id); 0088 d->basePath = QFileInfo(themeQmlPath).dir().absolutePath(); 0089 emit idChanged(); 0090 } 0091 } 0092 0093 QString Theme::name() const 0094 { 0095 return d->name; 0096 } 0097 0098 void Theme::setName(const QString& newValue) 0099 { 0100 if(newValue != d->name) { 0101 d->name = newValue; 0102 emit nameChanged(); 0103 } 0104 } 0105 0106 QVariantMap Theme::colors() const 0107 { 0108 return d->colors; 0109 } 0110 0111 void Theme::setColors(const QVariantMap& newValue) 0112 { 0113 if(newValue != d->colors) { 0114 d->colors = newValue; 0115 emit colorsChanged(); 0116 } 0117 } 0118 0119 QColor Theme::color(const QString& name) 0120 { 0121 if(d->colorCache.contains(name)) 0122 return d->colorCache.value(name); 0123 0124 QStringList parts = name.split('/'); 0125 QColor result; 0126 0127 if(!parts.isEmpty()) 0128 { 0129 QVariantMap map = d->colors; 0130 QString current = parts.takeFirst(); 0131 0132 while(map.contains(current)) 0133 { 0134 QVariant value = map.value(current); 0135 if(value.type() == QVariant::Map) 0136 { 0137 if(parts.isEmpty()) 0138 break; 0139 0140 map = value.toMap(); 0141 current = parts.takeFirst(); 0142 } 0143 else 0144 { 0145 result = value.value<QColor>(); 0146 map = QVariantMap(); 0147 } 0148 } 0149 } 0150 0151 if(!result.isValid()) { 0152 warnKrita << "Unable to find color" << name; 0153 } else { 0154 d->colorCache.insert(name, result); 0155 } 0156 0157 return result; 0158 } 0159 0160 QVariantMap Theme::sizes() const 0161 { 0162 return d->sizes; 0163 } 0164 0165 void Theme::setSizes(const QVariantMap& newValue) 0166 { 0167 if(newValue != d->sizes) { 0168 d->sizes = newValue; 0169 emit sizesChanged(); 0170 } 0171 } 0172 0173 float Theme::size(const QString& name) 0174 { 0175 Q_UNUSED(name); 0176 return 0.f; 0177 } 0178 0179 QVariantMap Theme::fonts() const 0180 { 0181 return d->fonts; 0182 } 0183 0184 void Theme::setFonts(const QVariantMap& newValue) 0185 { 0186 if(newValue != d->fonts) 0187 { 0188 d->fonts = newValue; 0189 0190 d->fontMap.clear(); 0191 0192 emit fontsChanged(); 0193 } 0194 } 0195 0196 void Theme::Private::loadFonts() 0197 { 0198 if (!fontsAdded) { 0199 QDir fontDir(basePath + '/' + fontPath); 0200 QStringList entries = fontDir.entryList(QDir::Files); 0201 QFontDatabase db; 0202 Q_FOREACH(QString entry, entries) { 0203 addedFonts.append(db.addApplicationFont(fontDir.absoluteFilePath(entry))); 0204 } 0205 fontsAdded = true; 0206 } 0207 } 0208 0209 QFont Theme::font(const QString& name) 0210 { 0211 d->loadFonts(); 0212 0213 if (d->fontMap.isEmpty()) { 0214 d->rebuildFontCache(); 0215 } 0216 0217 if (d->fontMap.contains(name)) 0218 return d->fontMap.value(name); 0219 0220 warnKrita << "Unable to find font" << name; 0221 return QFont(); 0222 } 0223 0224 QString Theme::fontPath() const 0225 { 0226 return d->fontPath; 0227 } 0228 0229 void Theme::setFontPath(const QString& newValue) 0230 { 0231 if(newValue != d->fontPath) { 0232 if(!d->addedFonts.isEmpty()) { 0233 QFontDatabase db; 0234 Q_FOREACH(int id, d->addedFonts) { 0235 db.removeApplicationFont(id); 0236 } 0237 d->addedFonts.clear(); 0238 } 0239 0240 d->fontPath = newValue; 0241 d->fontsAdded = false; 0242 0243 emit fontPathChanged(); 0244 } 0245 } 0246 0247 0248 QString Theme::iconPath() const 0249 { 0250 return d->iconPath; 0251 } 0252 0253 void Theme::setIconPath(const QString& newValue) 0254 { 0255 if(newValue != d->iconPath) { 0256 d->iconPath = newValue; 0257 emit iconPathChanged(); 0258 } 0259 } 0260 0261 QUrl Theme::icon(const QString& name) 0262 { 0263 QString url = QString("%1/%2/%3.svg").arg(d->basePath, d->iconPath, name); 0264 if(!QFile::exists(url)) { 0265 warnKrita << "Unable to find icon" << url; 0266 } 0267 0268 return QUrl::fromLocalFile(url); 0269 } 0270 0271 QString Theme::imagePath() const 0272 { 0273 return d->imagePath; 0274 } 0275 0276 void Theme::setImagePath(const QString& newValue) 0277 { 0278 if(newValue != d->imagePath) { 0279 d->imagePath = newValue; 0280 emit imagePathChanged(); 0281 } 0282 } 0283 0284 QUrl Theme::image(const QString& name) 0285 { 0286 QString url = QString("%1/%2/%3").arg(d->basePath, d->imagePath, name); 0287 if(!QFile::exists(url)) { 0288 warnKrita << "Unable to find image" << url; 0289 } 0290 0291 return QUrl::fromLocalFile(url); 0292 } 0293 0294 Theme* Theme::load(const QString& id, QQmlEngine *engine) 0295 { 0296 0297 QString qml = themePath(id); 0298 0299 QQmlComponent themeComponent(engine, 0); 0300 themeComponent.loadUrl(QUrl::fromLocalFile(qml), QQmlComponent::PreferSynchronous); 0301 0302 if (themeComponent.isError()) { 0303 warnKrita << themeComponent.errorString(); 0304 return 0; 0305 } 0306 0307 Theme* theme = qobject_cast<Theme*>(themeComponent.create()); 0308 if(!theme) { 0309 warnKrita << "Failed to create theme instance!"; 0310 return 0; 0311 } 0312 0313 return theme; 0314 } 0315 0316 bool Theme::eventFilter(QObject* target, QEvent* event) 0317 { 0318 if(target == qApp->activeWindow() && target->inherits("QMainWindow") && event->type() == QEvent::Resize) { 0319 d->rebuildFontCache(); 0320 emit fontCacheRebuilt(); 0321 } 0322 0323 return QObject::eventFilter(target, event); 0324 } 0325 0326 void Theme::Private::rebuildFontCache() 0327 { 0328 loadFonts(); 0329 fontMap.clear(); 0330 QFontDatabase db; 0331 for(QVariantMap::iterator itr = fonts.begin(); itr != fonts.end(); ++itr) 0332 { 0333 QVariantMap map = itr->toMap(); 0334 if(map.isEmpty()) 0335 continue; 0336 0337 QFont font = db.font(map.value("family").toString(), map.value("style", "Regular").toString(), 10); 0338 0339 if(font.isCopyOf(qApp->font())) 0340 warnKrita << "Could not find font" << map.value("family") << "with style" << map.value("style", "Regular"); 0341 0342 if (qApp->activeWindow()) { 0343 float lineCount = qApp->activeWindow()->height() > qApp->activeWindow()->width() ? lineCountPortrait : lineCountLandscape; 0344 float lineHeight = qApp->activeWindow()->height() / lineCount; 0345 font.setPixelSize(lineHeight * map.value("size", 1).toFloat()); 0346 } 0347 0348 fontMap.insert(itr.key(), font); 0349 } 0350 } 0351 0352 QString Theme::themePath(const QString &id) 0353 { 0354 QString qml = QStandardPaths::locate(QStandardPaths::AppDataLocation, 0355 QString("krita/qmlthemes/%1/theme.qml").arg(id)); 0356 if (qml.isEmpty()) { 0357 qml = QStandardPaths::locate(QStandardPaths::AppDataLocation, QString("qmlthemes/%1/theme.qml").arg(id)); 0358 } 0359 if (qml.isEmpty()) { 0360 qml = KoResourcePaths::getApplicationRoot() + QString("/share/krita/qmlthemes/%1/theme.qml").arg(id); 0361 } 0362 return qml; 0363 }