Warning, file /plasma/latte-dock/declarativeimports/core/iconitem.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2012 Marco Martin <mart@kde.org> 0003 SPDX-FileCopyrightText: 2014 David Edmundson <davidedmudnson@kde.org> 0004 SPDX-FileCopyrightText: 2016 Smith AR <audoban@openmailbox.org> 0005 SPDX-FileCopyrightText: 2016 Michail Vourlakos <mvourlakos@gmail.com> 0006 0007 This file is part of Latte-Dock and is a Fork of PlasmaCore::IconItem 0008 0009 SPDX-License-Identifier: GPL-2.0-or-later 0010 */ 0011 0012 #include "iconitem.h" 0013 0014 // local 0015 #include "extras.h" 0016 0017 // Qt 0018 #include <QDebug> 0019 #include <QPainter> 0020 #include <QPaintEngine> 0021 #include <QQuickWindow> 0022 #include <QPixmap> 0023 #include <QSGSimpleTextureNode> 0024 #include <QuickAddons/ManagedTextureNode> 0025 #include <QLatin1String> 0026 0027 // KDE 0028 #include <KIconTheme> 0029 #include <KIconThemes/KIconLoader> 0030 #include <KIconThemes/KIconEffect> 0031 0032 namespace Latte { 0033 0034 IconItem::IconItem(QQuickItem *parent) 0035 : QQuickItem(parent), 0036 m_active(false), 0037 m_smooth(false), 0038 m_textureChanged(false), 0039 m_sizeChanged(false), 0040 m_usesPlasmaTheme(false), 0041 m_lastValidSourceName(QString()), 0042 m_colorGroup(Plasma::Theme::NormalColorGroup) 0043 { 0044 setFlag(ItemHasContents, true); 0045 connect(KIconLoader::global(), SIGNAL(iconLoaderSettingsChanged()), 0046 this, SIGNAL(implicitWidthChanged())); 0047 connect(KIconLoader::global(), SIGNAL(iconLoaderSettingsChanged()), 0048 this, SIGNAL(implicitHeightChanged())); 0049 connect(this, &QQuickItem::enabledChanged, 0050 this, &IconItem::enabledChanged); 0051 connect(this, &QQuickItem::windowChanged, 0052 this, &IconItem::schedulePixmapUpdate); 0053 connect(this, SIGNAL(overlaysChanged()), 0054 this, SLOT(schedulePixmapUpdate())); 0055 connect(this, SIGNAL(providesColorsChanged()), 0056 this, SLOT(schedulePixmapUpdate())); 0057 0058 //initialize implicit size to the Dialog size 0059 setImplicitWidth(KIconLoader::global()->currentSize(KIconLoader::Dialog)); 0060 setImplicitHeight(KIconLoader::global()->currentSize(KIconLoader::Dialog)); 0061 setSmooth(true); 0062 } 0063 0064 IconItem::~IconItem() 0065 { 0066 } 0067 0068 void IconItem::setSource(const QVariant &source) 0069 { 0070 if (source == m_source) { 0071 return; 0072 } 0073 0074 m_source = source; 0075 QString sourceString = source.toString(); 0076 0077 // If the QIcon was created with QIcon::fromTheme(), try to load it as svg 0078 if (source.canConvert<QIcon>() && !source.value<QIcon>().name().isEmpty()) { 0079 sourceString = source.value<QIcon>().name(); 0080 } 0081 0082 if (!sourceString.isEmpty()) { 0083 setLastValidSourceName(sourceString); 0084 setLastLoadedSourceId(sourceString); 0085 0086 //If a url in the form file:// is passed, take the image pointed by that from disk 0087 QUrl url(sourceString); 0088 0089 if (url.isLocalFile()) { 0090 m_icon = QIcon(); 0091 m_imageIcon = QImage(url.path()); 0092 m_svgIconName.clear(); 0093 m_svgIcon.reset(); 0094 } else { 0095 if (!m_svgIcon) { 0096 m_svgIcon = std::make_unique<Plasma::Svg>(this); 0097 m_svgIcon->setColorGroup(m_colorGroup); 0098 m_svgIcon->setStatus(Plasma::Svg::Normal); 0099 m_svgIcon->setUsingRenderingCache(false); 0100 m_svgIcon->setDevicePixelRatio((window() ? window()->devicePixelRatio() : qApp->devicePixelRatio())); 0101 connect(m_svgIcon.get(), &Plasma::Svg::repaintNeeded, this, &IconItem::schedulePixmapUpdate); 0102 } 0103 0104 if (m_usesPlasmaTheme) { 0105 //try as a svg icon from plasma theme 0106 m_svgIcon->setImagePath(QLatin1String("icons/") + sourceString.split('-').first()); 0107 m_svgIcon->setContainsMultipleImages(true); 0108 //invalidate the image path to recalculate it later 0109 } else { 0110 m_svgIcon->setImagePath(QString()); 0111 } 0112 0113 //success? 0114 if (m_svgIcon->isValid() && m_svgIcon->hasElement(sourceString)) { 0115 m_icon = QIcon(); 0116 m_svgIconName = sourceString; 0117 //ok, svg not available from the plasma theme 0118 } else { 0119 //try to load from iconloader an svg with Plasma::Svg 0120 const auto *iconTheme = KIconLoader::global()->theme(); 0121 QString iconPath; 0122 0123 if (iconTheme) { 0124 iconPath = iconTheme->iconPath(sourceString + QLatin1String(".svg") 0125 , static_cast<int>(qMin(width(), height())) 0126 , KIconLoader::MatchBest); 0127 0128 if (iconPath.isEmpty()) { 0129 iconPath = iconTheme->iconPath(sourceString + QLatin1String(".svgz") 0130 , static_cast<int>(qMin(width(), height())) 0131 , KIconLoader::MatchBest); 0132 } 0133 } else { 0134 qWarning() << "KIconLoader has no theme set"; 0135 } 0136 0137 if (!iconPath.isEmpty()) { 0138 m_svgIcon->setImagePath(iconPath); 0139 m_svgIconName = sourceString; 0140 //fail, use QIcon 0141 } else { 0142 //if we started with a QIcon use that. 0143 m_icon = source.value<QIcon>(); 0144 0145 if (m_icon.isNull()) { 0146 m_icon = QIcon::fromTheme(sourceString); 0147 } 0148 0149 m_svgIconName.clear(); 0150 m_svgIcon.reset(); 0151 m_imageIcon = QImage(); 0152 } 0153 } 0154 } 0155 } else if (source.canConvert<QIcon>()) { 0156 m_icon = source.value<QIcon>(); 0157 m_iconCounter++; 0158 setLastLoadedSourceId("_icon_"+QString::number(m_iconCounter)); 0159 0160 m_imageIcon = QImage(); 0161 m_svgIconName.clear(); 0162 m_svgIcon.reset(); 0163 } else if (source.canConvert<QImage>()) { 0164 m_imageIcon = source.value<QImage>(); 0165 m_iconCounter++; 0166 setLastLoadedSourceId("_image_"+QString::number(m_iconCounter)); 0167 0168 m_icon = QIcon(); 0169 m_svgIconName.clear(); 0170 m_svgIcon.reset(); 0171 } else { 0172 m_icon = QIcon(); 0173 m_imageIcon = QImage(); 0174 m_svgIconName.clear(); 0175 m_svgIcon.reset(); 0176 } 0177 0178 if (width() > 0 && height() > 0) { 0179 schedulePixmapUpdate(); 0180 } 0181 0182 emit sourceChanged(); 0183 emit validChanged(); 0184 } 0185 0186 QVariant IconItem::source() const 0187 { 0188 return m_source; 0189 } 0190 0191 void IconItem::setLastLoadedSourceId(QString id) 0192 { 0193 if (m_lastLoadedSourceId == id) { 0194 return; 0195 } 0196 0197 m_lastLoadedSourceId = id; 0198 } 0199 0200 QString IconItem::lastValidSourceName() 0201 { 0202 return m_lastValidSourceName; 0203 } 0204 0205 void IconItem::setLastValidSourceName(QString name) 0206 { 0207 if (m_lastValidSourceName == name || name.isEmpty() || name == QLatin1String("application-x-executable")) { 0208 return; 0209 } 0210 0211 m_lastValidSourceName = name; 0212 0213 emit lastValidSourceNameChanged(); 0214 } 0215 0216 void IconItem::setColorGroup(Plasma::Theme::ColorGroup group) 0217 { 0218 if (m_colorGroup == group) { 0219 return; 0220 } 0221 0222 m_colorGroup = group; 0223 0224 if (m_svgIcon) { 0225 m_svgIcon->setColorGroup(group); 0226 } 0227 0228 emit colorGroupChanged(); 0229 } 0230 0231 Plasma::Theme::ColorGroup IconItem::colorGroup() const 0232 { 0233 return m_colorGroup; 0234 } 0235 0236 0237 void IconItem::setOverlays(const QStringList &overlays) 0238 { 0239 if (overlays == m_overlays) { 0240 return; 0241 } 0242 0243 m_overlays = overlays; 0244 emit overlaysChanged(); 0245 } 0246 0247 QStringList IconItem::overlays() const 0248 { 0249 return m_overlays; 0250 } 0251 0252 0253 bool IconItem::isActive() const 0254 { 0255 return m_active; 0256 } 0257 0258 void IconItem::setActive(bool active) 0259 { 0260 if (m_active == active) { 0261 return; 0262 } 0263 0264 m_active = active; 0265 0266 if (isComponentComplete()) { 0267 schedulePixmapUpdate(); 0268 } 0269 0270 emit activeChanged(); 0271 } 0272 0273 bool IconItem::providesColors() const 0274 { 0275 return m_providesColors; 0276 } 0277 0278 void IconItem::setProvidesColors(const bool provides) 0279 { 0280 if (m_providesColors == provides) { 0281 return; 0282 } 0283 0284 m_providesColors = provides; 0285 emit providesColorsChanged(); 0286 } 0287 0288 void IconItem::setSmooth(const bool smooth) 0289 { 0290 if (smooth == m_smooth) { 0291 return; 0292 } 0293 0294 m_smooth = smooth; 0295 update(); 0296 } 0297 0298 bool IconItem::smooth() const 0299 { 0300 return m_smooth; 0301 } 0302 0303 bool IconItem::isValid() const 0304 { 0305 return !m_icon.isNull() || m_svgIcon || !m_imageIcon.isNull(); 0306 } 0307 0308 int IconItem::paintedWidth() const 0309 { 0310 return boundingRect().size().toSize().width(); 0311 } 0312 0313 int IconItem::paintedHeight() const 0314 { 0315 return boundingRect().size().toSize().height(); 0316 } 0317 0318 bool IconItem::usesPlasmaTheme() const 0319 { 0320 return m_usesPlasmaTheme; 0321 } 0322 0323 void IconItem::setUsesPlasmaTheme(bool usesPlasmaTheme) 0324 { 0325 if (m_usesPlasmaTheme == usesPlasmaTheme) { 0326 return; 0327 } 0328 0329 m_usesPlasmaTheme = usesPlasmaTheme; 0330 0331 // Reload icon with new settings 0332 const QVariant src = m_source; 0333 m_source.clear(); 0334 setSource(src); 0335 0336 update(); 0337 emit usesPlasmaThemeChanged(); 0338 } 0339 0340 void IconItem::updatePolish() 0341 { 0342 QQuickItem::updatePolish(); 0343 loadPixmap(); 0344 } 0345 0346 QSGNode *IconItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) 0347 { 0348 Q_UNUSED(updatePaintNodeData) 0349 0350 if (m_iconPixmap.isNull() || width() < 1.0 || height() < 1.0) { 0351 delete oldNode; 0352 return nullptr; 0353 } 0354 0355 ManagedTextureNode *textureNode = dynamic_cast<ManagedTextureNode *>(oldNode); 0356 0357 if (!textureNode || m_textureChanged) { 0358 if (oldNode) 0359 delete oldNode; 0360 0361 textureNode = new ManagedTextureNode; 0362 textureNode->setTexture(QSharedPointer<QSGTexture>(window()->createTextureFromImage(m_iconPixmap.toImage(), QQuickWindow::TextureCanUseAtlas))); 0363 textureNode->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest); 0364 0365 m_sizeChanged = true; 0366 m_textureChanged = false; 0367 } 0368 0369 if (m_sizeChanged) { 0370 const auto iconSize = qMin(boundingRect().size().width(), boundingRect().size().height()); 0371 const QRectF destRect(QPointF(boundingRect().center() - QPointF(iconSize / 2, iconSize / 2)), QSizeF(iconSize, iconSize)); 0372 textureNode->setRect(destRect); 0373 m_sizeChanged = false; 0374 } 0375 0376 return textureNode; 0377 } 0378 0379 void IconItem::schedulePixmapUpdate() 0380 { 0381 polish(); 0382 } 0383 0384 void IconItem::enabledChanged() 0385 { 0386 schedulePixmapUpdate(); 0387 } 0388 0389 QColor IconItem::backgroundColor() const 0390 { 0391 return m_backgroundColor; 0392 } 0393 0394 void IconItem::setBackgroundColor(QColor background) 0395 { 0396 if (m_backgroundColor == background) { 0397 return; 0398 } 0399 0400 m_backgroundColor = background; 0401 emit backgroundColorChanged(); 0402 } 0403 0404 QColor IconItem::glowColor() const 0405 { 0406 return m_glowColor; 0407 } 0408 0409 void IconItem::setGlowColor(QColor glow) 0410 { 0411 if (m_glowColor == glow) { 0412 return; 0413 } 0414 0415 m_glowColor = glow; 0416 emit glowColorChanged(); 0417 } 0418 0419 void IconItem::updateColors() 0420 { 0421 QImage icon = m_iconPixmap.toImage(); 0422 0423 if (icon.format() != QImage::Format_Invalid) { 0424 float rtotal = 0, gtotal = 0, btotal = 0; 0425 float total = 0.0f; 0426 0427 for(int row=0; row<icon.height(); ++row) { 0428 QRgb *line = (QRgb *)icon.scanLine(row); 0429 0430 for(int col=0; col<icon.width(); ++col) { 0431 QRgb pix = line[col]; 0432 0433 int r = qRed(pix); 0434 int g = qGreen(pix); 0435 int b = qBlue(pix); 0436 int a = qAlpha(pix); 0437 0438 float saturation = (qMax(r, qMax(g, b)) - qMin(r, qMin(g, b))) / 255.0f; 0439 float relevance = .1 + .9 * (a / 255.0f) * saturation; 0440 0441 rtotal += (float)(r * relevance); 0442 gtotal += (float)(g * relevance); 0443 btotal += (float)(b * relevance); 0444 0445 total += relevance * 255; 0446 } 0447 } 0448 0449 int nr = (rtotal / total) * 255; 0450 int ng = (gtotal / total) * 255; 0451 int nb = (btotal / total) * 255; 0452 0453 QColor tempColor(nr, ng, nb); 0454 0455 if (tempColor.hsvSaturationF() > 0.15f) { 0456 tempColor.setHsvF(tempColor.hueF(), 0.65f, tempColor.valueF()); 0457 } 0458 0459 tempColor.setHsvF(tempColor.hueF(), tempColor.saturationF(), 0.55f); //original 0.90f ??? 0460 0461 setBackgroundColor(tempColor); 0462 0463 tempColor.setHsvF(tempColor.hueF(), tempColor.saturationF(), 1.0f); 0464 0465 setGlowColor(tempColor); 0466 } 0467 } 0468 0469 void IconItem::loadPixmap() 0470 { 0471 if (!isComponentComplete()) { 0472 return; 0473 } 0474 0475 const auto size = qMin(width(), height()); 0476 //final pixmap to paint 0477 QPixmap result; 0478 0479 if (size <= 0) { 0480 m_iconPixmap = QPixmap(); 0481 update(); 0482 return; 0483 } else if (m_svgIcon) { 0484 m_svgIcon->resize(size, size); 0485 0486 if (m_svgIcon->hasElement(m_svgIconName)) { 0487 result = m_svgIcon->pixmap(m_svgIconName); 0488 } else if (!m_svgIconName.isEmpty()) { 0489 const auto *iconTheme = KIconLoader::global()->theme(); 0490 QString iconPath; 0491 0492 if (iconTheme) { 0493 iconPath = iconTheme->iconPath(m_svgIconName + QLatin1String(".svg") 0494 , static_cast<int>(qMin(width(), height())) 0495 , KIconLoader::MatchBest); 0496 0497 if (iconPath.isEmpty()) { 0498 iconPath = iconTheme->iconPath(m_svgIconName + QLatin1String(".svgz"), 0499 static_cast<int>(qMin(width(), height())) 0500 , KIconLoader::MatchBest); 0501 } 0502 } else { 0503 qWarning() << "KIconLoader has no theme set"; 0504 } 0505 0506 if (!iconPath.isEmpty()) { 0507 m_svgIcon->setImagePath(iconPath); 0508 } 0509 0510 result = m_svgIcon->pixmap(); 0511 } 0512 } else if (!m_icon.isNull()) { 0513 result = m_icon.pixmap(QSize(static_cast<int>(size), static_cast<int>(size)) 0514 * (window() ? window()->devicePixelRatio() : qApp->devicePixelRatio())); 0515 } else if (!m_imageIcon.isNull()) { 0516 result = QPixmap::fromImage(m_imageIcon); 0517 } else { 0518 m_iconPixmap = QPixmap(); 0519 update(); 0520 return; 0521 } 0522 0523 // Strangely KFileItem::overlays() returns empty string-values, so 0524 // we need to check first whether an overlay must be drawn at all. 0525 // It is more efficient to do it here, as KIconLoader::drawOverlays() 0526 // assumes that an overlay will be drawn and has some additional 0527 // setup time. 0528 for (const QString &overlay : m_overlays) { 0529 if (!overlay.isEmpty()) { 0530 // There is at least one overlay, draw all overlays above m_pixmap 0531 // and cancel the check 0532 KIconLoader::global()->drawOverlays(m_overlays, result, KIconLoader::Desktop); 0533 break; 0534 } 0535 } 0536 0537 if (!isEnabled()) { 0538 result = KIconLoader::global()->iconEffect()->apply(result, KIconLoader::Desktop, KIconLoader::DisabledState); 0539 } else if (m_active) { 0540 result = KIconLoader::global()->iconEffect()->apply(result, KIconLoader::Desktop, KIconLoader::ActiveState); 0541 } 0542 0543 m_iconPixmap = result; 0544 0545 if (m_providesColors && m_lastLoadedSourceId != m_lastColorsSourceId) { 0546 m_lastColorsSourceId = m_lastLoadedSourceId; 0547 updateColors(); 0548 } 0549 0550 m_textureChanged = true; 0551 //don't animate initial setting 0552 update(); 0553 } 0554 0555 void IconItem::itemChange(ItemChange change, const ItemChangeData &value) 0556 { 0557 QQuickItem::itemChange(change, value); 0558 } 0559 0560 void IconItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) 0561 { 0562 if (newGeometry.size() != oldGeometry.size()) { 0563 m_sizeChanged = true; 0564 0565 if (newGeometry.width() > 1 && newGeometry.height() > 1) { 0566 schedulePixmapUpdate(); 0567 } else { 0568 update(); 0569 } 0570 0571 const auto oldSize = qMin(oldGeometry.size().width(), oldGeometry.size().height()); 0572 const auto newSize = qMin(newGeometry.size().width(), newGeometry.size().height()); 0573 0574 if (!almost_equal(oldSize, newSize, 2)) { 0575 emit paintedSizeChanged(); 0576 } 0577 } 0578 0579 QQuickItem::geometryChanged(newGeometry, oldGeometry); 0580 } 0581 0582 void IconItem::componentComplete() 0583 { 0584 QQuickItem::componentComplete(); 0585 schedulePixmapUpdate(); 0586 } 0587 0588 }