File indexing completed on 2024-12-08 10:16:05

0001 /*
0002     SPDX-FileCopyrightText: 2020-2022 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "styledframesvgitem.h"
0008 
0009 #include <QBuffer>
0010 #include <QDebug>
0011 #include <QFile>
0012 #include <QImageReader>
0013 #include <QPainter>
0014 #include <QQuickWindow>
0015 #include <QUrlQuery>
0016 #include <QXmlStreamReader>
0017 #include <QXmlStreamWriter>
0018 
0019 using namespace KPublicTransport;
0020 
0021 StyledFrameSvgItem::StyledFrameSvgItem(QQuickItem *parent)
0022     : QQuickPaintedItem(parent)
0023 {
0024     const auto updateFunc = [this]() { update(); };
0025     connect(this, &StyledFrameSvgItem::borderTopChanged, this, updateFunc);
0026     connect(this, &StyledFrameSvgItem::borderBottomChanged, this, updateFunc);
0027     connect(this, &StyledFrameSvgItem::borderLeftChanged, this, updateFunc);
0028     connect(this, &StyledFrameSvgItem::borderRightChanged, this, updateFunc);
0029     connect(this, &StyledFrameSvgItem::colorChanged, this, &StyledFrameSvgItem::invalidateImage);
0030     connect(this, &StyledFrameSvgItem::sourceChanged, this, &StyledFrameSvgItem::invalidateImage);
0031 
0032     setImplicitSize(48, 48);
0033 }
0034 
0035 StyledFrameSvgItem::~StyledFrameSvgItem() = default;
0036 
0037 void StyledFrameSvgItem::paint(QPainter *painter)
0038 {
0039     loadImage();
0040     if (m_image.isNull()) {
0041         return;
0042     }
0043 
0044     const auto dpr = window()->devicePixelRatio();
0045     const double xSrcOffsets[4] = { 0.0, m_borderLeft * dpr, m_image.width() - m_borderRight * dpr, (double)m_image.width() };
0046     const double ySrcOffsets[4] = { 0.0, m_borderTop * dpr, m_image.height() - m_borderBottom * dpr, (double)m_image.height() };
0047     const double xDestOffsets[4] = { 0.0, m_borderLeft, width() - m_borderRight, width() };
0048     const double yDestOffsets[4] = { 0.0, m_borderTop, height() - m_borderBottom, height() };
0049 
0050     for (int xi = 0; xi < 3; ++xi) {
0051         for (int yi = 0; yi < 3; ++yi) {
0052             QRectF srcRect(QPointF(xSrcOffsets[xi], ySrcOffsets[yi]), QPointF(xSrcOffsets[xi + 1], ySrcOffsets[yi + 1]));
0053             QRectF destRect(QPointF(xDestOffsets[xi], yDestOffsets[yi]), QPointF(xDestOffsets[xi + 1], yDestOffsets[yi + 1]));
0054             if (srcRect.width() <= 0 || srcRect.height() <= 0) {
0055                 continue;
0056             }
0057             painter->drawImage(destRect, m_image, srcRect);
0058         }
0059     }
0060 }
0061 
0062 void StyledFrameSvgItem::invalidateImage()
0063 {
0064     m_image = {};
0065     update();
0066 }
0067 
0068 void StyledFrameSvgItem::loadImage()
0069 {
0070     if (!m_image.isNull() || m_source.isEmpty()) {
0071         return;
0072     }
0073 
0074     // find SVG
0075     QFile svgFile(m_source);
0076     if (!svgFile.open(QFile::ReadOnly)) {
0077         qWarning() << "Failed to open SVG file:" << svgFile.fileName() << svgFile.errorString();
0078         return;
0079     }
0080 
0081     // prepare CSS
0082     QFile cssFile(QStringLiteral(":/org.kde.kpublictransport/ui/assets/style.css"));
0083     if (!cssFile.open(QFile::ReadOnly)) {
0084         qWarning() << "Failed to open CSS file:" << cssFile.fileName() << cssFile.errorString();
0085         return;
0086     }
0087     auto css = QString::fromUtf8(cssFile.readAll());
0088     css.replace(QLatin1String("{{fillColor}}"), m_fillColor.name(QColor::HexArgb));
0089     css.replace(QLatin1String("{{lineColor}}"), m_lineColor.name(QColor::HexArgb));
0090 
0091     // inject CSS (inspired by KIconLoader)
0092     QByteArray processedContents;
0093     QXmlStreamReader reader(&svgFile);
0094     QBuffer buffer(&processedContents);
0095     buffer.open(QIODevice::WriteOnly);
0096     QXmlStreamWriter writer(&buffer);
0097     while (!reader.atEnd()) {
0098         if (reader.readNext() == QXmlStreamReader::StartElement &&
0099             reader.qualifiedName() == QLatin1String("style") &&
0100             reader.attributes().value(QLatin1String("id")) == QLatin1String("current-color-scheme")) {
0101             writer.writeStartElement(QStringLiteral("style"));
0102             writer.writeAttributes(reader.attributes());
0103             writer.writeCharacters(css);
0104             writer.writeEndElement();
0105             while (reader.tokenType() != QXmlStreamReader::EndElement) {
0106                 reader.readNext();
0107             }
0108         } else if (reader.tokenType() != QXmlStreamReader::Invalid) {
0109             writer.writeCurrentToken(reader);
0110         }
0111     }
0112     buffer.close();
0113 
0114     // render SVG
0115     buffer.open(QIODevice::ReadOnly);
0116     buffer.seek(0);
0117     QImageReader imgReader(&buffer, "svg");
0118     imgReader.setScaledSize(imgReader.size() * window()->devicePixelRatio());
0119     m_image = imgReader.read();
0120     m_image.setDevicePixelRatio(window()->devicePixelRatio());
0121     setImplicitSize(imgReader.size().width(), m_image.size().height());
0122 }