mirror of https://github.com/nextcloud/desktop
Enable share to Talk and Email. Display correct icon. Added unit tests.
Signed-off-by: allexzander <blackslayer4@gmail.com>
This commit is contained in:
parent
3f6defe594
commit
a3fc812539
|
@ -149,4 +149,4 @@ trigger:
|
||||||
- master
|
- master
|
||||||
event:
|
event:
|
||||||
- pull_request
|
- pull_request
|
||||||
- push
|
- push
|
|
@ -104,6 +104,7 @@ set(client_SRCS
|
||||||
elidedlabel.cpp
|
elidedlabel.cpp
|
||||||
headerbanner.cpp
|
headerbanner.cpp
|
||||||
iconjob.cpp
|
iconjob.cpp
|
||||||
|
iconutils.cpp
|
||||||
remotewipe.cpp
|
remotewipe.cpp
|
||||||
tray/ActivityData.cpp
|
tray/ActivityData.cpp
|
||||||
tray/ActivityListModel.cpp
|
tray/ActivityListModel.cpp
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
#include "iconutils.h"
|
||||||
|
|
||||||
|
#include <theme.h>
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPixmapCache>
|
||||||
|
#include <QSvgRenderer>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) by Oleksandr Zolotov <alex@nextcloud.com>
|
||||||
|
*
|
||||||
|
* 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 <QColor>
|
||||||
|
#include <QPixmap>
|
||||||
|
|
||||||
|
namespace OCC {
|
||||||
|
namespace Ui {
|
||||||
|
namespace IconUtils {
|
||||||
|
QPixmap pixmapForBackground(const QString &fileName, const QColor &backgroundColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // ICONUTILS_H
|
|
@ -296,7 +296,7 @@ UserGroupShare::UserGroupShare(AccountPtr account,
|
||||||
, _note(note)
|
, _note(note)
|
||||||
, _expireDate(expireDate)
|
, _expireDate(expireDate)
|
||||||
{
|
{
|
||||||
Q_ASSERT(shareType == TypeUser || shareType == TypeGroup || shareType == TypeEmail);
|
Q_ASSERT(shareType == TypeUser || shareType == TypeGroup || shareType == TypeEmail || shareType == TypeRoom);
|
||||||
Q_ASSERT(shareWith);
|
Q_ASSERT(shareWith);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,6 +326,11 @@ QDate UserGroupShare::getExpireDate() const
|
||||||
|
|
||||||
void UserGroupShare::setExpireDate(const QDate &date)
|
void UserGroupShare::setExpireDate(const QDate &date)
|
||||||
{
|
{
|
||||||
|
if (_expireDate == date) {
|
||||||
|
emit expireDateSet();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto *job = new OcsShareJob(_account);
|
auto *job = new OcsShareJob(_account);
|
||||||
connect(job, &OcsShareJob::shareJobFinished, this, &UserGroupShare::slotExpireDateSet);
|
connect(job, &OcsShareJob::shareJobFinished, this, &UserGroupShare::slotExpireDateSet);
|
||||||
connect(job, &OcsJob::ocsError, this, &UserGroupShare::slotOcsError);
|
connect(job, &OcsJob::ocsError, this, &UserGroupShare::slotOcsError);
|
||||||
|
@ -461,7 +466,7 @@ void ShareManager::slotSharesFetched(const QJsonDocument &reply)
|
||||||
|
|
||||||
if (shareType == Share::TypeLink) {
|
if (shareType == Share::TypeLink) {
|
||||||
newShare = parseLinkShare(data);
|
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);
|
newShare = parseUserGroupShare(data);
|
||||||
} else {
|
} else {
|
||||||
newShare = parseShare(data);
|
newShare = parseShare(data);
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "thumbnailjob.h"
|
#include "thumbnailjob.h"
|
||||||
#include "sharemanager.h"
|
#include "sharemanager.h"
|
||||||
#include "theme.h"
|
#include "theme.h"
|
||||||
|
#include "iconutils.h"
|
||||||
|
|
||||||
#include "QProgressIndicator.h"
|
#include "QProgressIndicator.h"
|
||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
|
@ -46,6 +47,7 @@
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QListWidget>
|
#include <QListWidget>
|
||||||
|
#include <QSvgRenderer>
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
@ -247,7 +249,7 @@ void ShareUserGroupWidget::slotSharesFetched(const QList<QSharedPointer<Share>>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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<UserGroupShare>(share);
|
auto userGroupShare = qSharedPointerDynamicCast<UserGroupShare>(share);
|
||||||
auto *s = new ShareUserLine(_account, userGroupShare, _maxSharingPermissions, _isFile, _parentScrollArea);
|
auto *s = new ShareUserLine(_account, userGroupShare, _maxSharingPermissions, _isFile, _parentScrollArea);
|
||||||
connect(s, &ShareUserLine::resizeRequested, this, &ShareUserGroupWidget::slotAdjustScrollWidgetSize);
|
connect(s, &ShareUserLine::resizeRequested, this, &ShareUserGroupWidget::slotAdjustScrollWidgetSize);
|
||||||
|
@ -501,7 +503,6 @@ ShareUserLine::ShareUserLine(AccountPtr account,
|
||||||
_ui->permissionsEdit->setEnabled(enabled);
|
_ui->permissionsEdit->setEnabled(enabled);
|
||||||
connect(_ui->permissionsEdit, &QAbstractButton::clicked, this, &ShareUserLine::slotEditPermissionsChanged);
|
connect(_ui->permissionsEdit, &QAbstractButton::clicked, this, &ShareUserLine::slotEditPermissionsChanged);
|
||||||
connect(_ui->noteConfirmButton, &QAbstractButton::clicked, this, &ShareUserLine::onNoteConfirmButtonClicked);
|
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(_ui->calendar, &QDateTimeEdit::dateChanged, this, &ShareUserLine::setExpireDate);
|
||||||
|
|
||||||
connect(_share.data(), &UserGroupShare::noteSet, this, &ShareUserLine::disableProgessIndicatorAnimation);
|
connect(_share.data(), &UserGroupShare::noteSet, this, &ShareUserLine::disableProgessIndicatorAnimation);
|
||||||
|
@ -521,10 +522,9 @@ ShareUserLine::ShareUserLine(AccountPtr account,
|
||||||
|
|
||||||
showNoteOptions(false);
|
showNoteOptions(false);
|
||||||
|
|
||||||
// email shares do not support notes and expiration dates
|
const bool isNoteSupported = _share->getShareType() != Share::ShareType::TypeEmail && _share->getShareType() != Share::ShareType::TypeRoom;
|
||||||
const bool isNoteAndExpirationDateSupported = _share->getShareType() != Share::ShareType::TypeEmail;
|
|
||||||
|
|
||||||
if (isNoteAndExpirationDateSupported) {
|
if (isNoteSupported) {
|
||||||
_noteLinkAction = new QAction(tr("Note to recipient"));
|
_noteLinkAction = new QAction(tr("Note to recipient"));
|
||||||
_noteLinkAction->setCheckable(true);
|
_noteLinkAction->setCheckable(true);
|
||||||
menu->addAction(_noteLinkAction);
|
menu->addAction(_noteLinkAction);
|
||||||
|
@ -537,7 +537,9 @@ ShareUserLine::ShareUserLine(AccountPtr account,
|
||||||
|
|
||||||
showExpireDateOptions(false);
|
showExpireDateOptions(false);
|
||||||
|
|
||||||
if (isNoteAndExpirationDateSupported) {
|
const bool isExpirationDateSupported = _share->getShareType() != Share::ShareType::TypeEmail;
|
||||||
|
|
||||||
|
if (isExpirationDateSupported) {
|
||||||
// email shares do not support expiration dates
|
// email shares do not support expiration dates
|
||||||
_expirationDateLinkAction = new QAction(tr("Set expiration date"));
|
_expirationDateLinkAction = new QAction(tr("Set expiration date"));
|
||||||
_expirationDateLinkAction->setCheckable(true);
|
_expirationDateLinkAction->setCheckable(true);
|
||||||
|
@ -545,9 +547,8 @@ ShareUserLine::ShareUserLine(AccountPtr account,
|
||||||
connect(_expirationDateLinkAction, &QAction::triggered, this, &ShareUserLine::toggleExpireDateOptions);
|
connect(_expirationDateLinkAction, &QAction::triggered, this, &ShareUserLine::toggleExpireDateOptions);
|
||||||
const auto expireDate = _share->getExpireDate().isValid() ? share.data()->getExpireDate() : QDate();
|
const auto expireDate = _share->getExpireDate().isValid() ? share.data()->getExpireDate() : QDate();
|
||||||
if (!expireDate.isNull()) {
|
if (!expireDate.isNull()) {
|
||||||
_ui->calendar->setDate(expireDate);
|
|
||||||
_expirationDateLinkAction->setChecked(true);
|
_expirationDateLinkAction->setChecked(true);
|
||||||
showExpireDateOptions(true);
|
showExpireDateOptions(true, expireDate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -646,28 +647,7 @@ void ShareUserLine::loadAvatar()
|
||||||
_ui->avatar->setMaximumWidth(avatarSize);
|
_ui->avatar->setMaximumWidth(avatarSize);
|
||||||
_ui->avatar->setAlignment(Qt::AlignCenter);
|
_ui->avatar->setAlignment(Qt::AlignCenter);
|
||||||
|
|
||||||
/* Create the fallback avatar.
|
setDefaultAvatar(avatarSize);
|
||||||
*
|
|
||||||
* 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<quint8>(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());
|
|
||||||
|
|
||||||
/* Start the network job to fetch the avatar data.
|
/* 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)
|
void ShareUserLine::slotAvatarLoaded(QImage avatar)
|
||||||
{
|
{
|
||||||
if (avatar.isNull())
|
if (avatar.isNull())
|
||||||
|
@ -926,13 +938,57 @@ void ShareUserLine::customizeStyle()
|
||||||
_deleteShareButton->setIcon(deleteicon);
|
_deleteShareButton->setIcon(deleteicon);
|
||||||
|
|
||||||
_ui->noteConfirmButton->setIcon(Theme::createColorAwareIcon(":/client/theme/confirm.svg"));
|
_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));
|
_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
|
// 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);
|
_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<quint8>(hash[0]) / 255.;
|
||||||
|
return QColor::fromHslF(hue, 0.7, 0.68);
|
||||||
|
};
|
||||||
|
|
||||||
|
return calculateBackgroundBasedOnText();
|
||||||
|
}
|
||||||
|
|
||||||
void ShareUserLine::showNoteOptions(bool show)
|
void ShareUserLine::showNoteOptions(bool show)
|
||||||
{
|
{
|
||||||
_ui->noteLabel->setVisible(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->expirationLabel->setVisible(show);
|
||||||
_ui->calendar->setVisible(show);
|
_ui->calendar->setVisible(show);
|
||||||
_ui->confirmExpirationDate->setVisible(show);
|
|
||||||
|
|
||||||
if (show) {
|
if (show) {
|
||||||
const QDate date = QDate::currentDate().addDays(1);
|
_ui->calendar->setMinimumDate(QDate::currentDate().addDays(1));
|
||||||
_ui->calendar->setDate(date);
|
_ui->calendar->setDate(initialDate.isValid() ? initialDate : _ui->calendar->minimumDate());
|
||||||
_ui->calendar->setMinimumDate(date);
|
|
||||||
_ui->calendar->setFocus();
|
_ui->calendar->setFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -169,15 +169,19 @@ private slots:
|
||||||
private:
|
private:
|
||||||
void displayPermissions();
|
void displayPermissions();
|
||||||
void loadAvatar();
|
void loadAvatar();
|
||||||
|
void setDefaultAvatar(int avatarSize);
|
||||||
void customizeStyle();
|
void customizeStyle();
|
||||||
|
|
||||||
|
QPixmap pixmapForShareeType(Sharee::Type type, const QColor &backgroundColor = QColor()) const;
|
||||||
|
QColor backgroundColorForShareeType(Sharee::Type type) const;
|
||||||
|
|
||||||
void showNoteOptions(bool show);
|
void showNoteOptions(bool show);
|
||||||
void toggleNoteOptions(bool enable);
|
void toggleNoteOptions(bool enable);
|
||||||
void onNoteConfirmButtonClicked();
|
void onNoteConfirmButtonClicked();
|
||||||
void setNote(const QString ¬e);
|
void setNote(const QString ¬e);
|
||||||
|
|
||||||
void toggleExpireDateOptions(bool enable);
|
void toggleExpireDateOptions(bool enable);
|
||||||
void showExpireDateOptions(bool show);
|
void showExpireDateOptions(bool show, const QDate &initialDate = QDate());
|
||||||
void setExpireDate();
|
void setExpireDate();
|
||||||
|
|
||||||
void togglePasswordSetProgressAnimation(bool show);
|
void togglePasswordSetProgressAnimation(bool show);
|
||||||
|
|
|
@ -273,20 +273,6 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<widget class="QToolButton" name="confirmExpirationDate">
|
|
||||||
<property name="text">
|
|
||||||
<string notr="true">…</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../theme.qrc">
|
|
||||||
<normaloff>:/client/theme/confirm.svg</normaloff>:/client/theme/confirm.svg</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="autoRaise">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
|
|
@ -197,7 +197,7 @@ QIcon Theme::themeIcon(const QString &name, bool sysTray) const
|
||||||
return cached = QIcon::fromTheme(name);
|
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);
|
QSvgRenderer renderer(svgName);
|
||||||
const auto createPixmapFromSvg = [&renderer] (int size) {
|
const auto createPixmapFromSvg = [&renderer] (int size) {
|
||||||
QImage img(size, size, QImage::Format_ARGB32);
|
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 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);
|
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
|
// branded client may have several sizes of the same icon
|
||||||
const QString filePath = (useSvg || size <= 0)
|
const QString filePath = (useSvg || size <= 0)
|
||||||
? QString::fromLatin1(":/client/theme/%1/%2").arg(flavor).arg(name)
|
? QString(Theme::themePrefix) + QString::fromLatin1("%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-%3").arg(flavor).arg(name).arg(size);
|
||||||
|
|
||||||
const QString svgPath = filePath + ".svg";
|
const QString svgPath = filePath + ".svg";
|
||||||
if (useSvg) {
|
if (useSvg) {
|
||||||
|
@ -274,8 +274,7 @@ bool Theme::isHidpi(QPaintDevice *dev)
|
||||||
|
|
||||||
QIcon Theme::uiThemeIcon(const QString &iconName, bool uiHasDarkBg) const
|
QIcon Theme::uiThemeIcon(const QString &iconName, bool uiHasDarkBg) const
|
||||||
{
|
{
|
||||||
QString themeResBasePath = ":/client/theme/";
|
QString iconPath = QString(Theme::themePrefix) + (uiHasDarkBg ? "white/" : "black/") + iconName;
|
||||||
QString iconPath = themeResBasePath + (uiHasDarkBg?"white/":"black/") + iconName;
|
|
||||||
std::string icnPath = iconPath.toUtf8().constData();
|
std::string icnPath = iconPath.toUtf8().constData();
|
||||||
return QIcon(QPixmap(iconPath));
|
return QIcon(QPixmap(iconPath));
|
||||||
}
|
}
|
||||||
|
@ -303,8 +302,7 @@ QString Theme::hidpiFileName(const QString &iconName, const QColor &backgroundCo
|
||||||
{
|
{
|
||||||
const auto isDarkBackground = Theme::isDarkColor(backgroundColor);
|
const auto isDarkBackground = Theme::isDarkColor(backgroundColor);
|
||||||
|
|
||||||
const QString themeResBasePath = ":/client/theme/";
|
const QString iconPath = QString(Theme::themePrefix) + (isDarkBackground ? "white/" : "black/") + iconName;
|
||||||
const QString iconPath = themeResBasePath + (isDarkBackground ? "white/" : "black/") + iconName;
|
|
||||||
|
|
||||||
return Theme::hidpiFileName(iconPath, dev);
|
return Theme::hidpiFileName(iconPath, dev);
|
||||||
}
|
}
|
||||||
|
@ -406,7 +404,7 @@ bool Theme::systrayUseMonoIcons() const
|
||||||
|
|
||||||
bool Theme::monoIconsAvailable() 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();
|
return QDir(themeDir).exists();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -510,7 +508,7 @@ QVariant Theme::customMedia(CustomMediaType type)
|
||||||
break;
|
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)) {
|
if (QFile::exists(imgPath)) {
|
||||||
QPixmap pix(imgPath);
|
QPixmap pix(imgPath);
|
||||||
if (pix.isNull()) {
|
if (pix.isNull()) {
|
||||||
|
@ -581,11 +579,11 @@ QColor Theme::wizardHeaderBackgroundColor() const
|
||||||
QPixmap Theme::wizardApplicationLogo() const
|
QPixmap Theme::wizardApplicationLogo() const
|
||||||
{
|
{
|
||||||
if (!Theme::isBranded()) {
|
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
|
#ifdef APPLICATION_WIZARD_USE_CUSTOM_LOGO
|
||||||
const auto useSvg = shouldPreferSvg();
|
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) {
|
if (useSvg) {
|
||||||
const auto maxHeight = Theme::isHidpi() ? 200 : 100;
|
const auto maxHeight = Theme::isHidpi() ? 200 : 100;
|
||||||
const auto maxWidth = 2 * maxHeight;
|
const auto maxWidth = 2 * maxHeight;
|
||||||
|
@ -605,7 +603,7 @@ QPixmap Theme::wizardHeaderLogo() const
|
||||||
{
|
{
|
||||||
#ifdef APPLICATION_WIZARD_USE_CUSTOM_LOGO
|
#ifdef APPLICATION_WIZARD_USE_CUSTOM_LOGO
|
||||||
const auto useSvg = shouldPreferSvg();
|
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) {
|
if (useSvg) {
|
||||||
const auto maxHeight = 64;
|
const auto maxHeight = 64;
|
||||||
const auto maxWidth = 2 * maxHeight;
|
const auto maxWidth = 2 * maxHeight;
|
||||||
|
|
|
@ -535,6 +535,8 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual bool showVirtualFilesOption() const;
|
virtual bool showVirtualFilesOption() const;
|
||||||
|
|
||||||
|
static constexpr const char *themePrefix = ":/client/theme/";
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
#ifndef TOKEN_AUTH_ONLY
|
#ifndef TOKEN_AUTH_ONLY
|
||||||
QIcon themeIcon(const QString &name, bool sysTray = false) const;
|
QIcon themeIcon(const QString &name, bool sysTray = false) const;
|
||||||
|
|
|
@ -57,6 +57,7 @@ nextcloud_add_test(FolderWatcher)
|
||||||
nextcloud_add_test(Capabilities)
|
nextcloud_add_test(Capabilities)
|
||||||
nextcloud_add_test(PushNotifications)
|
nextcloud_add_test(PushNotifications)
|
||||||
nextcloud_add_test(Theme)
|
nextcloud_add_test(Theme)
|
||||||
|
nextcloud_add_test(IconUtils)
|
||||||
nextcloud_add_test(NotificationCache)
|
nextcloud_add_test(NotificationCache)
|
||||||
|
|
||||||
if( UNIX AND NOT APPLE )
|
if( UNIX AND NOT APPLE )
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) by Oleksandr Zolotov <alex@nextcloud.com>
|
||||||
|
*
|
||||||
|
* 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 <QTest>
|
||||||
|
|
||||||
|
#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<QString>(blackImages.begin(), blackImages.end()).subtract(QSet<QString>(whiteImages.begin(), whiteImages.end()));
|
||||||
|
const auto whiteImagesExclusive = QSet<QString>(whiteImages.begin(), whiteImages.end()).subtract(QSet<QString>(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"
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
#include "theme.h"
|
#include "theme.h"
|
||||||
#include "themeutils.h"
|
#include "themeutils.h"
|
||||||
|
#include "iconutils.h"
|
||||||
|
|
||||||
class TestTheme : public QObject
|
class TestTheme : public QObject
|
||||||
{
|
{
|
||||||
|
|
|
@ -198,5 +198,6 @@
|
||||||
<file>theme/colored/user-status-invisible.svg</file>
|
<file>theme/colored/user-status-invisible.svg</file>
|
||||||
<file>theme/colored/user-status-away.svg</file>
|
<file>theme/colored/user-status-away.svg</file>
|
||||||
<file>theme/colored/user-status-dnd.svg</file>
|
<file>theme/colored/user-status-dnd.svg</file>
|
||||||
|
<file>theme/black/email.svg</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
|
@ -198,5 +198,6 @@
|
||||||
<file>theme/colored/user-status-invisible.svg</file>
|
<file>theme/colored/user-status-invisible.svg</file>
|
||||||
<file>theme/colored/user-status-away.svg</file>
|
<file>theme/colored/user-status-away.svg</file>
|
||||||
<file>theme/colored/user-status-dnd.svg</file>
|
<file>theme/colored/user-status-dnd.svg</file>
|
||||||
|
<file>theme/black/email.svg</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 24 24" width="16px" fill="#000000"><path d="M0 0h24v24H0z" fill="none"/><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg>
|
After Width: | Height: | Size: 267 B |
Loading…
Reference in New Issue