diff --git a/.drone.yml b/.drone.yml index 51e9636e5..2ebea87b8 100644 --- a/.drone.yml +++ b/.drone.yml @@ -149,4 +149,4 @@ trigger: - master event: - pull_request - - push + - push \ No newline at end of file diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 29de7ca98..67a2c7323 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -104,6 +104,7 @@ set(client_SRCS elidedlabel.cpp headerbanner.cpp iconjob.cpp + iconutils.cpp remotewipe.cpp tray/ActivityData.cpp tray/ActivityListModel.cpp diff --git a/src/gui/iconutils.cpp b/src/gui/iconutils.cpp new file mode 100644 index 000000000..82706e521 --- /dev/null +++ b/src/gui/iconutils.cpp @@ -0,0 +1,92 @@ +#include "iconutils.h" + +#include + +#include +#include +#include +#include + +namespace OCC { +namespace Ui { +namespace IconUtils { +QPixmap pixmapForBackground(const QString &fileName, const QColor &backgroundColor) +{ + Q_ASSERT(!fileName.isEmpty()); + + // some icons are present in white or black only, so, we need to check both when needed + const auto iconBaseColors = QStringList({ QStringLiteral("black"), QStringLiteral("white") }); + + const QString pixmapColor = backgroundColor.isValid() && !Theme::isDarkColor(backgroundColor) ? "black" : "white"; + + const QString cacheKey = fileName + QLatin1Char(',') + pixmapColor; + + QPixmap cachedPixmap; + + if (!QPixmapCache::find(cacheKey, &cachedPixmap)) { + if (iconBaseColors.contains(pixmapColor)) { + cachedPixmap = QPixmap::fromImage(QImage(QString(Theme::themePrefix) + pixmapColor + QLatin1Char('/') + fileName)); + QPixmapCache::insert(cacheKey, cachedPixmap); + return cachedPixmap; + } + + const auto drawSvgWithCustomFillColor = [](const QString &sourceSvgPath, const QString &fillColor) { + QSvgRenderer svgRenderer; + + if (!svgRenderer.load(sourceSvgPath)) { + return QPixmap(); + } + + // render source image + QImage svgImage(svgRenderer.defaultSize(), QImage::Format_ARGB32); + { + QPainter svgImagePainter(&svgImage); + svgImage.fill(Qt::GlobalColor::transparent); + svgRenderer.render(&svgImagePainter); + } + + // draw target image with custom fillColor + QImage image(svgRenderer.defaultSize(), QImage::Format_ARGB32); + image.fill(QColor(fillColor)); + { + QPainter imagePainter(&image); + imagePainter.setCompositionMode(QPainter::CompositionMode_DestinationIn); + imagePainter.drawImage(0, 0, svgImage); + } + + return QPixmap::fromImage(image); + }; + + // find the first matching svg among base colors, if any + const QString sourceSvg = [&]() { + for (const auto &color : iconBaseColors) { + const QString baseSVG(QString(Theme::themePrefix) + color + QLatin1Char('/') + fileName); + + if (QFile(baseSVG).exists()) { + return baseSVG; + } + } + return QString(); + }(); + + Q_ASSERT(!sourceSvg.isEmpty()); + if (sourceSvg.isEmpty()) { + qWarning("Failed to find base svg for %s", qPrintable(cacheKey)); + return {}; + } + + cachedPixmap = drawSvgWithCustomFillColor(sourceSvg, pixmapColor); + QPixmapCache::insert(cacheKey, cachedPixmap); + + Q_ASSERT(!cachedPixmap.isNull()); + if (cachedPixmap.isNull()) { + qWarning("Failed to load pixmap for %s", qPrintable(cacheKey)); + return {}; + } + } + + return cachedPixmap; +} +} +} +} diff --git a/src/gui/iconutils.h b/src/gui/iconutils.h new file mode 100644 index 000000000..136510a8a --- /dev/null +++ b/src/gui/iconutils.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) by Oleksandr Zolotov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef ICONUTILS_H +#define ICONUTILS_H + +#include +#include + +namespace OCC { +namespace Ui { +namespace IconUtils { +QPixmap pixmapForBackground(const QString &fileName, const QColor &backgroundColor); +} +} +} +#endif // ICONUTILS_H diff --git a/src/gui/sharemanager.cpp b/src/gui/sharemanager.cpp index 6f89eb573..dbeab8625 100644 --- a/src/gui/sharemanager.cpp +++ b/src/gui/sharemanager.cpp @@ -296,7 +296,7 @@ UserGroupShare::UserGroupShare(AccountPtr account, , _note(note) , _expireDate(expireDate) { - Q_ASSERT(shareType == TypeUser || shareType == TypeGroup || shareType == TypeEmail); + Q_ASSERT(shareType == TypeUser || shareType == TypeGroup || shareType == TypeEmail || shareType == TypeRoom); Q_ASSERT(shareWith); } @@ -326,6 +326,11 @@ QDate UserGroupShare::getExpireDate() const void UserGroupShare::setExpireDate(const QDate &date) { + if (_expireDate == date) { + emit expireDateSet(); + return; + } + auto *job = new OcsShareJob(_account); connect(job, &OcsShareJob::shareJobFinished, this, &UserGroupShare::slotExpireDateSet); connect(job, &OcsJob::ocsError, this, &UserGroupShare::slotOcsError); @@ -461,7 +466,7 @@ void ShareManager::slotSharesFetched(const QJsonDocument &reply) if (shareType == Share::TypeLink) { newShare = parseLinkShare(data); - } else if (shareType == Share::TypeGroup || shareType == Share::TypeUser || shareType == Share::TypeEmail) { + } else if (shareType == Share::TypeGroup || shareType == Share::TypeUser || shareType == Share::TypeEmail || shareType == Share::TypeRoom) { newShare = parseUserGroupShare(data); } else { newShare = parseShare(data); diff --git a/src/gui/shareusergroupwidget.cpp b/src/gui/shareusergroupwidget.cpp index 47e6ebc6c..ce28fbb64 100644 --- a/src/gui/shareusergroupwidget.cpp +++ b/src/gui/shareusergroupwidget.cpp @@ -27,6 +27,7 @@ #include "thumbnailjob.h" #include "sharemanager.h" #include "theme.h" +#include "iconutils.h" #include "QProgressIndicator.h" #include @@ -46,6 +47,7 @@ #include #include #include +#include #include @@ -247,7 +249,7 @@ void ShareUserGroupWidget::slotSharesFetched(const QList> } - Q_ASSERT(share->getShareType() == Share::TypeUser || share->getShareType() == Share::TypeGroup || share->getShareType() == Share::TypeEmail); + Q_ASSERT(share->getShareType() == Share::TypeUser || share->getShareType() == Share::TypeGroup || share->getShareType() == Share::TypeEmail || share->getShareType() == Share::TypeRoom); auto userGroupShare = qSharedPointerDynamicCast(share); auto *s = new ShareUserLine(_account, userGroupShare, _maxSharingPermissions, _isFile, _parentScrollArea); connect(s, &ShareUserLine::resizeRequested, this, &ShareUserGroupWidget::slotAdjustScrollWidgetSize); @@ -501,7 +503,6 @@ ShareUserLine::ShareUserLine(AccountPtr account, _ui->permissionsEdit->setEnabled(enabled); connect(_ui->permissionsEdit, &QAbstractButton::clicked, this, &ShareUserLine::slotEditPermissionsChanged); connect(_ui->noteConfirmButton, &QAbstractButton::clicked, this, &ShareUserLine::onNoteConfirmButtonClicked); - connect(_ui->confirmExpirationDate, &QAbstractButton::clicked, this, &ShareUserLine::setExpireDate); connect(_ui->calendar, &QDateTimeEdit::dateChanged, this, &ShareUserLine::setExpireDate); connect(_share.data(), &UserGroupShare::noteSet, this, &ShareUserLine::disableProgessIndicatorAnimation); @@ -521,10 +522,9 @@ ShareUserLine::ShareUserLine(AccountPtr account, showNoteOptions(false); - // email shares do not support notes and expiration dates - const bool isNoteAndExpirationDateSupported = _share->getShareType() != Share::ShareType::TypeEmail; + const bool isNoteSupported = _share->getShareType() != Share::ShareType::TypeEmail && _share->getShareType() != Share::ShareType::TypeRoom; - if (isNoteAndExpirationDateSupported) { + if (isNoteSupported) { _noteLinkAction = new QAction(tr("Note to recipient")); _noteLinkAction->setCheckable(true); menu->addAction(_noteLinkAction); @@ -537,7 +537,9 @@ ShareUserLine::ShareUserLine(AccountPtr account, showExpireDateOptions(false); - if (isNoteAndExpirationDateSupported) { + const bool isExpirationDateSupported = _share->getShareType() != Share::ShareType::TypeEmail; + + if (isExpirationDateSupported) { // email shares do not support expiration dates _expirationDateLinkAction = new QAction(tr("Set expiration date")); _expirationDateLinkAction->setCheckable(true); @@ -545,9 +547,8 @@ ShareUserLine::ShareUserLine(AccountPtr account, connect(_expirationDateLinkAction, &QAction::triggered, this, &ShareUserLine::toggleExpireDateOptions); const auto expireDate = _share->getExpireDate().isValid() ? share.data()->getExpireDate() : QDate(); if (!expireDate.isNull()) { - _ui->calendar->setDate(expireDate); _expirationDateLinkAction->setChecked(true); - showExpireDateOptions(true); + showExpireDateOptions(true, expireDate); } } @@ -646,28 +647,7 @@ void ShareUserLine::loadAvatar() _ui->avatar->setMaximumWidth(avatarSize); _ui->avatar->setAlignment(Qt::AlignCenter); - /* Create the fallback avatar. - * - * This will be shown until the avatar image data arrives. - */ - const QByteArray hash = QCryptographicHash::hash(_ui->sharedWith->text().toUtf8(), QCryptographicHash::Md5); - double hue = static_cast(hash[0]) / 255.; - - // See core/js/placeholder.js for details on colors and styling - const QColor bg = QColor::fromHslF(hue, 0.7, 0.68); - const QString style = QString(R"(* { - color: #fff; - background-color: %1; - border-radius: %2px; - text-align: center; - line-height: %2px; - font-size: %2px; - })").arg(bg.name(), QString::number(avatarSize / 2)); - _ui->avatar->setStyleSheet(style); - - // The avatar label is the first character of the user name. - const QString text = _share->getShareWith()->displayName(); - _ui->avatar->setText(text.at(0).toUpper()); + setDefaultAvatar(avatarSize); /* Start the network job to fetch the avatar data. * @@ -680,6 +660,38 @@ void ShareUserLine::loadAvatar() } } +void ShareUserLine::setDefaultAvatar(int avatarSize) +{ + /* Create the fallback avatar. + * + * This will be shown until the avatar image data arrives. + */ + + // See core/js/placeholder.js for details on colors and styling + const auto backgroundColor = backgroundColorForShareeType(_share->getShareWith()->type()); + const QString style = QString(R"(* { + color: #fff; + background-color: %1; + border-radius: %2px; + text-align: center; + line-height: %2px; + font-size: %2px; + })").arg(backgroundColor.name(), QString::number(avatarSize / 2)); + _ui->avatar->setStyleSheet(style); + + const auto pixmap = pixmapForShareeType(_share->getShareWith()->type(), backgroundColor); + + if (!pixmap.isNull()) { + _ui->avatar->setPixmap(pixmap); + } else { + qCDebug(lcSharing) << "pixmap is null for share type: " << _share->getShareWith()->type(); + + // The avatar label is the first character of the user name. + const auto text = _share->getShareWith()->displayName(); + _ui->avatar->setText(text.at(0).toUpper()); + } +} + void ShareUserLine::slotAvatarLoaded(QImage avatar) { if (avatar.isNull()) @@ -926,13 +938,57 @@ void ShareUserLine::customizeStyle() _deleteShareButton->setIcon(deleteicon); _ui->noteConfirmButton->setIcon(Theme::createColorAwareIcon(":/client/theme/confirm.svg")); - _ui->confirmExpirationDate->setIcon(Theme::createColorAwareIcon(":/client/theme/confirm.svg")); _ui->progressIndicator->setColor(QGuiApplication::palette().color(QPalette::WindowText)); // make sure to force BackgroundRole to QPalette::WindowText for a lable, because it's parent always has a different role set that applies to children unless customized _ui->errorLabel->setBackgroundRole(QPalette::WindowText); } +QPixmap ShareUserLine::pixmapForShareeType(Sharee::Type type, const QColor &backgroundColor) const +{ + switch (type) { + case Sharee::Room: + return Ui::IconUtils::pixmapForBackground(QStringLiteral("talk-app.svg"), backgroundColor); + case Sharee::Email: + return Ui::IconUtils::pixmapForBackground(QStringLiteral("email.svg"), backgroundColor); + case Sharee::Group: + case Sharee::Federated: + case Sharee::Circle: + case Sharee::User: + break; + } + + return {}; +} + +QColor ShareUserLine::backgroundColorForShareeType(Sharee::Type type) const +{ + switch (type) { + case Sharee::Room: + return Theme::instance()->wizardHeaderBackgroundColor(); + case Sharee::Email: + return Theme::instance()->wizardHeaderTitleColor(); + case Sharee::Group: + case Sharee::Federated: + case Sharee::Circle: + case Sharee::User: + break; + } + + const auto calculateBackgroundBasedOnText = [this]() { + const auto hash = QCryptographicHash::hash(_ui->sharedWith->text().toUtf8(), QCryptographicHash::Md5); + Q_ASSERT(hash.size() > 0); + if (hash.size() == 0) { + qCWarning(lcSharing) << "Failed to calculate hash color for share:" << _share->path(); + return QColor{}; + } + const double hue = static_cast(hash[0]) / 255.; + return QColor::fromHslF(hue, 0.7, 0.68); + }; + + return calculateBackgroundBasedOnText(); +} + void ShareUserLine::showNoteOptions(bool show) { _ui->noteLabel->setVisible(show); @@ -979,16 +1035,14 @@ void ShareUserLine::toggleExpireDateOptions(bool enable) } } -void ShareUserLine::showExpireDateOptions(bool show) +void ShareUserLine::showExpireDateOptions(bool show, const QDate &initialDate) { _ui->expirationLabel->setVisible(show); _ui->calendar->setVisible(show); - _ui->confirmExpirationDate->setVisible(show); if (show) { - const QDate date = QDate::currentDate().addDays(1); - _ui->calendar->setDate(date); - _ui->calendar->setMinimumDate(date); + _ui->calendar->setMinimumDate(QDate::currentDate().addDays(1)); + _ui->calendar->setDate(initialDate.isValid() ? initialDate : _ui->calendar->minimumDate()); _ui->calendar->setFocus(); } diff --git a/src/gui/shareusergroupwidget.h b/src/gui/shareusergroupwidget.h index 75a74590e..8715af22f 100644 --- a/src/gui/shareusergroupwidget.h +++ b/src/gui/shareusergroupwidget.h @@ -169,15 +169,19 @@ private slots: private: void displayPermissions(); void loadAvatar(); + void setDefaultAvatar(int avatarSize); void customizeStyle(); + QPixmap pixmapForShareeType(Sharee::Type type, const QColor &backgroundColor = QColor()) const; + QColor backgroundColorForShareeType(Sharee::Type type) const; + void showNoteOptions(bool show); void toggleNoteOptions(bool enable); void onNoteConfirmButtonClicked(); void setNote(const QString ¬e); void toggleExpireDateOptions(bool enable); - void showExpireDateOptions(bool show); + void showExpireDateOptions(bool show, const QDate &initialDate = QDate()); void setExpireDate(); void togglePasswordSetProgressAnimation(bool show); diff --git a/src/gui/shareuserline.ui b/src/gui/shareuserline.ui index 5c811ed57..e79614a06 100644 --- a/src/gui/shareuserline.ui +++ b/src/gui/shareuserline.ui @@ -273,20 +273,6 @@ - - - - - - - - :/client/theme/confirm.svg:/client/theme/confirm.svg - - - true - - - diff --git a/src/libsync/theme.cpp b/src/libsync/theme.cpp index 920ffed04..d66fa8d81 100644 --- a/src/libsync/theme.cpp +++ b/src/libsync/theme.cpp @@ -197,7 +197,7 @@ QIcon Theme::themeIcon(const QString &name, bool sysTray) const return cached = QIcon::fromTheme(name); } - const auto svgName = QString::fromLatin1(":/client/theme/%1/%2.svg").arg(flavor).arg(name); + const QString svgName = QString(Theme::themePrefix) + QString::fromLatin1("%1/%2.svg").arg(flavor).arg(name); QSvgRenderer renderer(svgName); const auto createPixmapFromSvg = [&renderer] (int size) { QImage img(size, size, QImage::Format_ARGB32); @@ -208,7 +208,7 @@ QIcon Theme::themeIcon(const QString &name, bool sysTray) const }; const auto loadPixmap = [flavor, name] (int size) { - const auto pixmapName = QString::fromLatin1(":/client/theme/%1/%2-%3.png").arg(flavor).arg(name).arg(size); + const QString pixmapName = QString(Theme::themePrefix) + QString::fromLatin1("%1/%2-%3.png").arg(flavor).arg(name).arg(size); return QPixmap(pixmapName); }; @@ -249,8 +249,8 @@ QString Theme::themeImagePath(const QString &name, int size, bool sysTray) const // branded client may have several sizes of the same icon const QString filePath = (useSvg || size <= 0) - ? QString::fromLatin1(":/client/theme/%1/%2").arg(flavor).arg(name) - : QString::fromLatin1(":/client/theme/%1/%2-%3").arg(flavor).arg(name).arg(size); + ? QString(Theme::themePrefix) + QString::fromLatin1("%1/%2").arg(flavor).arg(name) + : QString(Theme::themePrefix) + QString::fromLatin1("%1/%2-%3").arg(flavor).arg(name).arg(size); const QString svgPath = filePath + ".svg"; if (useSvg) { @@ -274,8 +274,7 @@ bool Theme::isHidpi(QPaintDevice *dev) QIcon Theme::uiThemeIcon(const QString &iconName, bool uiHasDarkBg) const { - QString themeResBasePath = ":/client/theme/"; - QString iconPath = themeResBasePath + (uiHasDarkBg?"white/":"black/") + iconName; + QString iconPath = QString(Theme::themePrefix) + (uiHasDarkBg ? "white/" : "black/") + iconName; std::string icnPath = iconPath.toUtf8().constData(); return QIcon(QPixmap(iconPath)); } @@ -303,8 +302,7 @@ QString Theme::hidpiFileName(const QString &iconName, const QColor &backgroundCo { const auto isDarkBackground = Theme::isDarkColor(backgroundColor); - const QString themeResBasePath = ":/client/theme/"; - const QString iconPath = themeResBasePath + (isDarkBackground ? "white/" : "black/") + iconName; + const QString iconPath = QString(Theme::themePrefix) + (isDarkBackground ? "white/" : "black/") + iconName; return Theme::hidpiFileName(iconPath, dev); } @@ -406,7 +404,7 @@ bool Theme::systrayUseMonoIcons() const bool Theme::monoIconsAvailable() const { - QString themeDir = QString::fromLatin1(":/client/theme/%1/").arg(Theme::instance()->systrayIconFlavor(true)); + QString themeDir = QString(Theme::themePrefix) + QString::fromLatin1("%1/").arg(Theme::instance()->systrayIconFlavor(true)); return QDir(themeDir).exists(); } @@ -510,7 +508,7 @@ QVariant Theme::customMedia(CustomMediaType type) break; } - QString imgPath = QString::fromLatin1(":/client/theme/colored/%1.png").arg(key); + QString imgPath = QString(Theme::themePrefix) + QString::fromLatin1("colored/%1.png").arg(key); if (QFile::exists(imgPath)) { QPixmap pix(imgPath); if (pix.isNull()) { @@ -581,11 +579,11 @@ QColor Theme::wizardHeaderBackgroundColor() const QPixmap Theme::wizardApplicationLogo() const { if (!Theme::isBranded()) { - return QPixmap(Theme::hidpiFileName(":/client/theme/colored/wizard-nextcloud.png")); + return QPixmap(Theme::hidpiFileName(QString(Theme::themePrefix) + "colored/wizard-nextcloud.png")); } #ifdef APPLICATION_WIZARD_USE_CUSTOM_LOGO const auto useSvg = shouldPreferSvg(); - const auto logoBasePath = QStringLiteral(":/client/theme/colored/wizard_logo"); + const QString logoBasePath = QString(Theme::themePrefix) + QStringLiteral("colored/wizard_logo"); if (useSvg) { const auto maxHeight = Theme::isHidpi() ? 200 : 100; const auto maxWidth = 2 * maxHeight; @@ -605,7 +603,7 @@ QPixmap Theme::wizardHeaderLogo() const { #ifdef APPLICATION_WIZARD_USE_CUSTOM_LOGO const auto useSvg = shouldPreferSvg(); - const auto logoBasePath = QStringLiteral(":/client/theme/colored/wizard_logo"); + const QString logoBasePath = QString(Theme::themePrefix) + QStringLiteral("colored/wizard_logo"); if (useSvg) { const auto maxHeight = 64; const auto maxWidth = 2 * maxHeight; diff --git a/src/libsync/theme.h b/src/libsync/theme.h index f47aebe63..c0e2433e5 100644 --- a/src/libsync/theme.h +++ b/src/libsync/theme.h @@ -535,6 +535,8 @@ public: */ virtual bool showVirtualFilesOption() const; + static constexpr const char *themePrefix = ":/client/theme/"; + protected: #ifndef TOKEN_AUTH_ONLY QIcon themeIcon(const QString &name, bool sysTray = false) const; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 33bf1ac71..924520941 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -57,6 +57,7 @@ nextcloud_add_test(FolderWatcher) nextcloud_add_test(Capabilities) nextcloud_add_test(PushNotifications) nextcloud_add_test(Theme) +nextcloud_add_test(IconUtils) nextcloud_add_test(NotificationCache) if( UNIX AND NOT APPLE ) diff --git a/test/testiconutils.cpp b/test/testiconutils.cpp new file mode 100644 index 000000000..a25e9f0e6 --- /dev/null +++ b/test/testiconutils.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) by Oleksandr Zolotov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include + +#include "theme.h" +#include "iconutils.h" + +class TestIconUtils : public QObject +{ + Q_OBJECT + +public: + TestIconUtils() + { + Q_INIT_RESOURCE(resources); + Q_INIT_RESOURCE(theme); + } + +private slots: + void testPixmapForBackground() + { + const QDir blackSvgDir(QString(OCC::Theme::themePrefix) + QStringLiteral("black")); + const QStringList blackImages = blackSvgDir.entryList(QStringList("*.svg")); + + const QDir whiteSvgDir(QString(OCC::Theme::themePrefix) + QStringLiteral("white")); + const QStringList whiteImages = whiteSvgDir.entryList(QStringList("*.svg")); + + if (blackImages.size() > 0) { + // white pixmap for dark background - should not fail + QVERIFY(!OCC::Ui::IconUtils::pixmapForBackground(whiteImages.at(0), QColor("blue")).isNull()); + } + + if (whiteImages.size() > 0) { + // black pixmap for bright background - should not fail + QVERIFY(!OCC::Ui::IconUtils::pixmapForBackground(blackImages.at(0), QColor("yellow")).isNull()); + } + + const auto blackImagesExclusive = QSet(blackImages.begin(), blackImages.end()).subtract(QSet(whiteImages.begin(), whiteImages.end())); + const auto whiteImagesExclusive = QSet(whiteImages.begin(), whiteImages.end()).subtract(QSet(blackImages.begin(), blackImages.end())); + + if (blackImagesExclusive != whiteImagesExclusive) { + // black pixmap for dark background - should fail as we don't have this image in black + QVERIFY(OCC::Ui::IconUtils::pixmapForBackground(blackImagesExclusive.values().at(0), QColor("blue")).isNull()); + + // white pixmap for bright background - should fail as we don't have this image in white + QVERIFY(OCC::Ui::IconUtils::pixmapForBackground(whiteImagesExclusive.values().at(0), QColor("yellow")).isNull()); + } + } +}; + +QTEST_MAIN(TestIconUtils) +#include "testiconutils.moc" diff --git a/test/testtheme.cpp b/test/testtheme.cpp index 52bc7b324..c50766f92 100644 --- a/test/testtheme.cpp +++ b/test/testtheme.cpp @@ -16,6 +16,7 @@ #include "theme.h" #include "themeutils.h" +#include "iconutils.h" class TestTheme : public QObject { diff --git a/theme.qrc b/theme.qrc index 8da6ba56b..dae295a00 100644 --- a/theme.qrc +++ b/theme.qrc @@ -198,5 +198,6 @@ theme/colored/user-status-invisible.svg theme/colored/user-status-away.svg theme/colored/user-status-dnd.svg + theme/black/email.svg diff --git a/theme.qrc.in b/theme.qrc.in index 63da54883..0638c4853 100644 --- a/theme.qrc.in +++ b/theme.qrc.in @@ -198,5 +198,6 @@ theme/colored/user-status-invisible.svg theme/colored/user-status-away.svg theme/colored/user-status-dnd.svg + theme/black/email.svg diff --git a/theme/black/email.svg b/theme/black/email.svg new file mode 100644 index 000000000..56fd74cf8 --- /dev/null +++ b/theme/black/email.svg @@ -0,0 +1 @@ +