#include "./syncthingicons.h" #include "resources/qtconfig.h" // use meta-data of syncthingtray application here #include "resources/../../tray/resources/config.h" #include #include #include #include #include #include #include #include #include #include #ifndef LIB_SYNCTHING_MODEL_STATIC ENABLE_QT_RESOURCES_OF_STATIC_DEPENDENCIES #endif 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, StatusIconStrokeWidth strokeWidth) { // serialize colors auto gradientStartColor = colors.backgroundStart.name(QColor::HexRgb); auto gradientEndColor = colors.backgroundEnd.name(QColor::HexRgb); constexpr auto opaque = static_cast(1.0); if (colors.backgroundStart.alphaF() < opaque) { gradientStartColor += QStringLiteral(";stop-opacity:") + QString::number(static_cast(colors.backgroundStart.alphaF())); } if (colors.backgroundEnd.alphaF() < opaque) { gradientEndColor += QStringLiteral(";stop-opacity:") + QString::number(static_cast(colors.backgroundEnd.alphaF())); } auto fillColor = colors.foreground.name(QColor::HexRgb); auto strokeColor = fillColor; if (colors.foreground.alphaF() < opaque) { const auto alpha = QString::number(static_cast(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( "" "" "" "" ), }; static const auto normalStrokeWidth = QStringLiteral("0.81771719"), thickStrokeWidth = QStringLiteral("1.22"); static const auto normalCircleRadius = QStringLiteral("1.22"), largeCircleRadius = QStringLiteral("1.5"); const auto &emblemData = emblems[static_cast(statusEmblem)]; const auto &strokeWidthF = strokeWidth == StatusIconStrokeWidth::Normal ? normalStrokeWidth : thickStrokeWidth; const auto &circleRadius = strokeWidth == StatusIconStrokeWidth::Normal ? normalCircleRadius : largeCircleRadius; return (QStringLiteral( "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "") % emblemData % QStringLiteral( "" )).toUtf8(); // clang-format on } /// \cond namespace Detail { template QPixmap renderSvgImage(const SourceType &source, const QSize &givenSize, int margin) { const auto scaleFactor = #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) !QCoreApplication::testAttribute(Qt::AA_UseHighDpiPixmaps) ? 1.0 : #endif qGuiApp->devicePixelRatio(); const auto scaledSize = QSize(givenSize * scaleFactor); auto renderer = QSvgRenderer(source); auto renderSize = QSize(renderer.defaultSize()); renderSize.scale(scaledSize.width() - margin, scaledSize.height() - margin, Qt::KeepAspectRatio); auto renderBounds = QRect(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()); } auto pm = QPixmap(scaledSize); pm.fill(QColor(Qt::transparent)); auto painter = QPainter(&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); } 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, settings.strokeWidth), settings.renderSize))) , idling(QIcon(renderSvgImage(makeSyncthingIcon(settings.idleColor, StatusEmblem::None, settings.strokeWidth), settings.renderSize))) , scanninig(QIcon(renderSvgImage(makeSyncthingIcon(settings.scanningColor, StatusEmblem::Scanning, settings.strokeWidth), settings.renderSize))) , notify(QIcon(renderSvgImage(makeSyncthingIcon(settings.warningColor, StatusEmblem::Alert, settings.strokeWidth), settings.renderSize))) , pause(QIcon(renderSvgImage(makeSyncthingIcon(settings.pausedColor, StatusEmblem::Paused, settings.strokeWidth), settings.renderSize))) , sync(QIcon( renderSvgImage(makeSyncthingIcon(settings.synchronizingColor, StatusEmblem::Synchronizing, settings.strokeWidth), settings.renderSize))) , syncComplete(QIcon(renderSvgImage(makeSyncthingIcon(settings.defaultColor, StatusEmblem::Complete, settings.strokeWidth), settings.renderSize))) , error(QIcon(renderSvgImage(makeSyncthingIcon(settings.errorColor, StatusEmblem::Alert, settings.strokeWidth), settings.renderSize))) , errorSync(QIcon(renderSvgImage(makeSyncthingIcon(settings.errorColor, StatusEmblem::Synchronizing, settings.strokeWidth), settings.renderSize))) , newItem(QIcon(renderSvgImage(makeSyncthingIcon(settings.defaultColor, StatusEmblem::Add, settings.strokeWidth), settings.renderSize))) , isValid(true) { } ForkAwesomeIcons::ForkAwesomeIcons(QtForkAwesome::Renderer &renderer, const QColor &color, const QSize &size) : hashtag(renderer.pixmap(QtForkAwesome::Icon::Hashtag, size, color)) , folderOpen(renderer.pixmap(QtForkAwesome::Icon::FolderOpen, size, color)) , globe(renderer.pixmap(QtForkAwesome::Icon::Globe, size, color)) , home(renderer.pixmap(QtForkAwesome::Icon::Home, size, color)) , shareAlt(renderer.pixmap(QtForkAwesome::Icon::ShareAlt, size, color)) , refresh(renderer.pixmap(QtForkAwesome::Icon::Refresh, size, color)) , clock(renderer.pixmap(QtForkAwesome::Icon::ClockO, size, color)) , exchangeAlt(renderer.pixmap(QtForkAwesome::Icon::Exchange, size, color)) , exclamationTriangle(renderer.pixmap(QtForkAwesome::Icon::ExclamationTriangle, size, color)) , cogs(renderer.pixmap(QtForkAwesome::Icon::Cogs, size, color)) , link(renderer.pixmap(QtForkAwesome::Icon::Link, size, color)) , eye(renderer.pixmap(QtForkAwesome::Icon::Eye, size, color)) , fileArchive(renderer.pixmap(QtForkAwesome::Icon::FileArchiveO, size, color)) , folder(renderer.pixmap(QtForkAwesome::Icon::Folder, size, color)) , certificate(renderer.pixmap(QtForkAwesome::Icon::Certificate, size, color)) , networkWired(renderer.pixmap(QtForkAwesome::Icon::Sitemap, size, color)) , cloudDownloadAlt(renderer.pixmap(QtForkAwesome::Icon::CloudDownload, size, color)) , cloudUploadAlt(renderer.pixmap(QtForkAwesome::Icon::CloudUpload, size, color)) , tag(renderer.pixmap(QtForkAwesome::Icon::Tag, size, color)) , exchange(renderer.pixmap(QtForkAwesome::Icon::Exchange, size, color)) { } IconManager::IconManager(const QPalette *palette) : m_statusIcons() , m_trayIcons(m_statusIcons) , m_commonForkAwesomeIcons( m_forkAwesomeRenderer, (palette ? *palette : QGuiApplication::palette()).color(QPalette::Normal, QPalette::Text), QSize(64, 64)) , m_usesApplicationPalette(palette == nullptr) { } void IconManager::setPalette(const QPalette &palette) { emit forkAwesomeIconsChanged( m_commonForkAwesomeIcons = ForkAwesomeIcons(m_forkAwesomeRenderer, palette.color(QPalette::Normal, QPalette::Text), QSize(64, 64))); } bool IconManager::event(QEvent *event) { switch (event->type()) { case QEvent::ApplicationPaletteChange: std::cout << "this is unfortunately never executed" << std::endl; setPalette(QGuiApplication::palette()); break; default: ; } return QObject::event(event); } IconManager &IconManager::instance(const QPalette *palette) { static auto iconManager = IconManager(palette); return iconManager; } QString aboutDialogAttribution() { return QStringLiteral( "

Developed by " APP_AUTHOR "
Fallback icons from KDE/Breeze " "project (copyright © 2014 Uri Herrera and others, see the according " "LGPL-3.0 license)" "
Syncthing icons from Syncthing project " "(copyright © 2014-2016 The Syncthing Authors, see the according " "MPL-2.0 license)" "
Using icons from Fork " "Awesome (see their license)

"); } QImage aboutDialogImage() { return renderSvgImage(makeSyncthingIcon(), QSize(128, 128)).toImage(); } void setForkAwesomeThemeOverrides() { auto &renderer = QtForkAwesome::Renderer::global(); renderer.addThemeOverride(QtForkAwesome::Icon::Folder, QStringLiteral("folder-symbolic")); renderer.addThemeOverride(QtForkAwesome::Icon::Sitemap, QStringLiteral("network-server-symbolic")); renderer.addThemeOverride(QtForkAwesome::Icon::Download, QStringLiteral("folder-download-symbolic")); renderer.addThemeOverride(QtForkAwesome::Icon::History, QStringLiteral("shallow-history")); renderer.addThemeOverride(QtForkAwesome::Icon::History, QStringLiteral("view-history")); renderer.addThemeOverride(QtForkAwesome::Icon::Refresh, QStringLiteral("view-refresh-symbolic")); renderer.addThemeOverride(QtForkAwesome::Icon::Pause, QStringLiteral("media-playback-pause")); renderer.addThemeOverride(QtForkAwesome::Icon::Play, QStringLiteral("media-playback-start")); renderer.addThemeOverride(QtForkAwesome::Icon::Stop, QStringLiteral("media-playback-stop")); renderer.addThemeOverride(QtForkAwesome::Icon::Home, QStringLiteral("user-home-symbolic")); renderer.addThemeOverride(QtForkAwesome::Icon::Globe, QStringLiteral("globe-symbolic")); renderer.addThemeOverride(QtForkAwesome::Icon::Info, QStringLiteral("help-about-symbolic")); renderer.addThemeOverride(QtForkAwesome::Icon::Cog, QStringLiteral("settings-configure")); renderer.addThemeOverride(QtForkAwesome::Icon::Cog, QStringLiteral("system-settings-symbolic")); renderer.addThemeOverride(QtForkAwesome::Icon::Plug, QStringLiteral("network-connect")); renderer.addThemeOverride(QtForkAwesome::Icon::Qrcode, QStringLiteral("qrscanner-symbolic")); renderer.addThemeOverride(QtForkAwesome::Icon::Qrcode, QStringLiteral("view-barcode-qr")); renderer.addThemeOverride(QtForkAwesome::Icon::PowerOff, QStringLiteral("system-shutdown-symbolic")); renderer.addThemeOverride(QtForkAwesome::Icon::FileText, QStringLiteral("terminal-symbolic")); renderer.addThemeOverride(QtForkAwesome::Icon::CloudUpload, QStringLiteral("cloud-upload")); renderer.addThemeOverride(QtForkAwesome::Icon::CloudDownload, QStringLiteral("cloud-download")); renderer.addThemeOverride(QtForkAwesome::Icon::Search, QStringLiteral("search")); } } // namespace Data