#include "./syncthingicons.h" #include #include #include #include #include #include namespace Data { /*! * \brief Generates the SVG code for the Syncthing icon with the specified \a colors and status emblem. */ QByteArray makeSyncthingIcon(const StatusIconColorSet &colors, StatusEmblem statusEmblem) { // serialize colors auto gradientStartColor = colors.backgroundStart.name(QColor::HexRgb); auto gradientEndColor = colors.backgroundEnd.name(QColor::HexRgb); if (colors.backgroundStart.alphaF() < 1.0) { gradientStartColor += QStringLiteral(";stop-opacity:") + QString::number(colors.backgroundStart.alphaF()); } if (colors.backgroundEnd.alphaF() < 1.0) { gradientEndColor += QStringLiteral(";stop-opacity:") + QString::number(colors.backgroundEnd.alphaF()); } auto fillColor = colors.foreground.name(QColor::HexRgb); auto strokeColor = fillColor; if (colors.foreground.alphaF() < 1.0) { const auto alpha = QString::number(colors.foreground.alphaF()); fillColor += QStringLiteral(";fill-opacity:") + alpha; strokeColor += QStringLiteral(";stroke-opacity:") + alpha; } // make SVG document // clang-format off static const QString emblems[] = { QString(), QStringLiteral( "" "" "" "" ), QStringLiteral( "" "" "" "" "" ), QStringLiteral( "" "" "" "" "" ), QStringLiteral( "" "" "" "" "" ), QStringLiteral( "" "" ), QStringLiteral( "" "" "" "" ), QStringLiteral( "" "" "" "" ), }; const auto &emblemData = emblems[static_cast(statusEmblem)]; return (QStringLiteral( "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "") % (emblemData.isEmpty() ? QString() : emblemData) % QStringLiteral( "" )).toUtf8(); // clang-format on } /// \cond namespace Detail { template QPixmap renderSvgImage(const SourceType &source, const QSize &givenSize, int margin) { const qreal scaleFactor = #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) !QCoreApplication::testAttribute(Qt::AA_UseHighDpiPixmaps) ? 1.0 : #endif qGuiApp->devicePixelRatio(); QSvgRenderer renderer(source); QSize scaledSize(static_cast(givenSize.width() * scaleFactor), static_cast(givenSize.height() * scaleFactor)); QSize renderSize(renderer.defaultSize()); renderSize.scale(scaledSize.width() - margin, scaledSize.height() - margin, Qt::KeepAspectRatio); QRect renderBounds(QPoint(), scaledSize); if (renderSize.width() < renderBounds.width()) { const auto diff = (renderBounds.width() - renderSize.width()) / 2; renderBounds.setX(diff); renderBounds.setWidth(renderSize.width()); } if (renderSize.height() < renderBounds.height()) { const auto diff = (renderBounds.height() - renderSize.height()) / 2; renderBounds.setY(diff); renderBounds.setHeight(renderSize.height()); } QPixmap pm(scaledSize); pm.fill(QColor(Qt::transparent)); QPainter painter(&pm); renderer.render(&painter, renderBounds); pm.setDevicePixelRatio(scaleFactor); return pm; } } // namespace Detail /// \endcond /*! * \brief Renders an SVG image to a QPixmap. * \remarks If instantiating QIcon directly from SVG image the icon is not displayed in the tray under Plasma 5. It works * with Tint2, however. */ QPixmap renderSvgImage(const QString &path, const QSize &size, int margin) { return Detail::renderSvgImage(path, size, margin); } /*! * \brief Renders an SVG image to a QPixmap. */ QPixmap renderSvgImage(const QByteArray &contents, const QSize &size, int margin) { return Detail::renderSvgImage(contents, size, margin); } /*! * \brief Returns the font awesome icon with the specified \a iconName and \a color. */ QByteArray loadFontAwesomeIcon(const QString &iconName, const QColor &color, bool solid) { QByteArray result; QFile icon(QString((solid ? QStringLiteral(":/icons/hicolor/scalable/fa/") : QStringLiteral(":/icons/hicolor/scalable/fa-non-solid/")) % iconName % QStringLiteral(".svg"))); if (!icon.open(QFile::ReadOnly)) { return result; } result = icon.readAll(); const auto pathBegin = result.indexOf(" 0) { result.insert(pathBegin + 6, (QStringLiteral("fill=\"") % color.name(QColor::HexRgb) % QStringLiteral("\" ")).toUtf8()); } return result; } StatusIconSettings::StatusIconSettings() : defaultColor({ QStringLiteral("#26B6DB"), QStringLiteral("#0882C8"), QStringLiteral("#FFFFFF") }) , errorColor({ QStringLiteral("#DB3C26"), QStringLiteral("#C80828"), QStringLiteral("#FFFFFF") }) , warningColor({ QStringLiteral("#c9ce3b"), QStringLiteral("#ebb83b"), QStringLiteral("#FFFFFF") }) , idleColor({ QStringLiteral("#2D9D69"), QStringLiteral("#2D9D69"), QStringLiteral("#FFFFFF") }) , scanningColor({ QStringLiteral("#26B6DB"), QStringLiteral("#0882C8"), QStringLiteral("#FFFFFF") }) , synchronizingColor({ QStringLiteral("#26B6DB"), QStringLiteral("#0882C8"), QStringLiteral("#FFFFFF") }) , pausedColor({ QStringLiteral("#A9A9A9"), QStringLiteral("#58656C"), QStringLiteral("#FFFFFF") }) , disconnectedColor({ QStringLiteral("#A9A9A9"), QStringLiteral("#58656C"), QStringLiteral("#FFFFFF") }) { } StatusIconSettings::StatusIconSettings(StatusIconSettings::DarkTheme) : defaultColor({ QStringLiteral("#00000000"), QStringLiteral("#00000000"), QStringLiteral("#FFFFFFFF") }) , errorColor({ QStringLiteral("#00000000"), QStringLiteral("#00000000"), QStringLiteral("#FFFFAEA5") }) , warningColor({ QStringLiteral("#00FFFFFF"), QStringLiteral("#00FFFFFF"), QStringLiteral("#FFFFF6A5") }) , idleColor({ QStringLiteral("#00FFFFFF"), QStringLiteral("#00FFFFFF"), QStringLiteral("#FFFFFFFF") }) , scanningColor({ QStringLiteral("#00FFFFFF"), QStringLiteral("#00FFFFFF"), QStringLiteral("#FFA5EFFF") }) , synchronizingColor({ QStringLiteral("#00FFFFFF"), QStringLiteral("#00FFFFFF"), QStringLiteral("#FFA5EFFF") }) , pausedColor({ QStringLiteral("#00000000"), QStringLiteral("#00FFFFFF"), QStringLiteral("#FFA7A7A7") }) , disconnectedColor({ QStringLiteral("#00000000"), QStringLiteral("#00000000"), QStringLiteral("#FFA7A7A7") }) { } StatusIconSettings::StatusIconSettings(StatusIconSettings::BrightTheme) : defaultColor({ QStringLiteral("#00FFFFFF"), QStringLiteral("#00FFFFFF"), QStringLiteral("#FF000000") }) , errorColor({ QStringLiteral("#00FFFFFF"), QStringLiteral("#00FFFFFF"), QStringLiteral("#FFDB3C26") }) , warningColor({ QStringLiteral("#00000000"), QStringLiteral("#00000000"), QStringLiteral("#FFC9CE3B") }) , idleColor({ QStringLiteral("#00000000"), QStringLiteral("#00000000"), QStringLiteral("#FF000000") }) , scanningColor({ QStringLiteral("#00000000"), QStringLiteral("#00000000"), QStringLiteral("#FF26B6DB") }) , synchronizingColor({ QStringLiteral("#00000000"), QStringLiteral("#00000000"), QStringLiteral("#FF26B6DB") }) , pausedColor({ QStringLiteral("#00FFFFFF"), QStringLiteral("#00FFFFFF"), QStringLiteral("#FFA7A7A7") }) , disconnectedColor({ QStringLiteral("#00FFFFFF"), QStringLiteral("#00FFFFFF"), QStringLiteral("#FFA7A7A7") }) { } std::vector StatusIconSettings::colorMapping() { return std::vector({ { QCoreApplication::translate("Data::StatusIconSettings", "Misc. notifications"), StatusEmblem::Complete, defaultColor }, { QCoreApplication::translate("Data::StatusIconSettings", "Error"), StatusEmblem::Alert, errorColor }, { QCoreApplication::translate("Data::StatusIconSettings", "Warning"), StatusEmblem::Alert, warningColor }, { QCoreApplication::translate("Data::StatusIconSettings", "Idle"), StatusEmblem::None, idleColor }, { QCoreApplication::translate("Data::StatusIconSettings", "Scanning"), StatusEmblem::Scanning, scanningColor }, { QCoreApplication::translate("Data::StatusIconSettings", "Synchronizing"), StatusEmblem::Synchronizing, synchronizingColor }, { QCoreApplication::translate("Data::StatusIconSettings", "Paused"), StatusEmblem::Paused, pausedColor }, { QCoreApplication::translate("Data::StatusIconSettings", "Disconnected"), StatusEmblem::None, disconnectedColor }, }); } StatusIconSettings::StatusIconSettings(const QString &str) : StatusIconSettings() { const auto parts = QtUtilities::splitRef(str, QChar(';')); int index = 0; for (auto *field : { &defaultColor, &errorColor, &warningColor, &idleColor, &scanningColor, &synchronizingColor, &pausedColor, &disconnectedColor }) { if (index >= parts.size()) { break; } const auto colors = parts[index].split(QChar(',')); if (colors.size() >= 2) { field->backgroundStart = colors[0].toString(); field->backgroundEnd = colors[1].toString(); } if (colors.size() >= 3) { field->foreground = colors[2].toString(); } ++index; } } QString StatusIconSettings::toString() const { QString res; res.reserve(128); for (auto *field : { &defaultColor, &errorColor, &warningColor, &idleColor, &scanningColor, &synchronizingColor, &pausedColor, &disconnectedColor }) { if (!res.isEmpty()) { res += QChar(';'); } res += field->backgroundStart.name(QColor::HexArgb) % QChar(',') % field->backgroundEnd.name(QColor::HexArgb) % QChar(',') % field->foreground.name(QColor::HexArgb); } return res; } StatusIcons::StatusIcons(const StatusIconSettings &settings) : disconnected(QIcon(renderSvgImage(makeSyncthingIcon(settings.disconnectedColor, StatusEmblem::None)))) , idling(QIcon(renderSvgImage(makeSyncthingIcon(settings.idleColor, StatusEmblem::None)))) , scanninig(QIcon(renderSvgImage(makeSyncthingIcon(settings.scanningColor, StatusEmblem::Scanning)))) , notify(QIcon(renderSvgImage(makeSyncthingIcon(settings.warningColor, StatusEmblem::Alert)))) , pause(QIcon(renderSvgImage(makeSyncthingIcon(settings.pausedColor, StatusEmblem::Paused)))) , sync(QIcon(renderSvgImage(makeSyncthingIcon(settings.synchronizingColor, StatusEmblem::Synchronizing)))) , syncComplete(QIcon(renderSvgImage(makeSyncthingIcon(settings.defaultColor, StatusEmblem::Complete)))) , error(QIcon(renderSvgImage(makeSyncthingIcon(settings.errorColor, StatusEmblem::Alert)))) , errorSync(QIcon(renderSvgImage(makeSyncthingIcon(settings.errorColor, StatusEmblem::Synchronizing)))) , newItem(QIcon(renderSvgImage(makeSyncthingIcon(settings.defaultColor, StatusEmblem::Add)))) , isValid(true) { } FontAwesomeIcons::FontAwesomeIcons(const QColor &color, const QSize &size, int margin) : hashtag(renderSvgImage(loadFontAwesomeIcon(QStringLiteral("hashtag"), color), size, margin)) , folderOpen(renderSvgImage(loadFontAwesomeIcon(QStringLiteral("folder-open"), color), size, margin)) , globe(renderSvgImage(loadFontAwesomeIcon(QStringLiteral("globe"), color), size, margin)) , home(renderSvgImage(loadFontAwesomeIcon(QStringLiteral("home"), color), size, margin)) , shareAlt(renderSvgImage(loadFontAwesomeIcon(QStringLiteral("share-alt"), color), size, margin)) , refresh(renderSvgImage(loadFontAwesomeIcon(QStringLiteral("redo"), color), size, margin)) , clock(renderSvgImage(loadFontAwesomeIcon(QStringLiteral("clock"), color), size, margin)) , exchangeAlt(renderSvgImage(loadFontAwesomeIcon(QStringLiteral("exchange-alt"), color), size, margin)) , exclamationTriangle(renderSvgImage(loadFontAwesomeIcon(QStringLiteral("exclamation-triangle"), color), size, margin)) , cogs(renderSvgImage(loadFontAwesomeIcon(QStringLiteral("cogs"), color), size, margin)) , link(renderSvgImage(loadFontAwesomeIcon(QStringLiteral("link"), color), size, margin)) , eye(renderSvgImage(loadFontAwesomeIcon(QStringLiteral("eye"), color), size, margin)) , fileArchive(renderSvgImage(loadFontAwesomeIcon(QStringLiteral("file-archive"), color), size, margin)) , folder(renderSvgImage(loadFontAwesomeIcon(QStringLiteral("folder"), color), size, margin)) , certificate(renderSvgImage(loadFontAwesomeIcon(QStringLiteral("certificate"), color), size, margin)) , networkWired(renderSvgImage(loadFontAwesomeIcon(QStringLiteral("network-wired"), color), size, margin)) , cloudDownloadAlt(renderSvgImage(loadFontAwesomeIcon(QStringLiteral("cloud-download-alt"), color), size, margin)) , cloudUploadAlt(renderSvgImage(loadFontAwesomeIcon(QStringLiteral("cloud-upload-alt"), color), size, margin)) , tag(renderSvgImage(loadFontAwesomeIcon(QStringLiteral("tag"), color), size, margin)) { } IconManager::IconManager() : m_statusIcons() , m_trayIcons(m_statusIcons) , m_fontAwesomeIconsForLightTheme(QColor(10, 10, 10), QSize(64, 64), 8) , m_fontAwesomeIconsForDarkTheme(Qt::white, QSize(64, 64), 8) { } IconManager &IconManager::instance() { static IconManager iconManager; return iconManager; } } // namespace Data