mirror of https://github.com/nextcloud/desktop
Fix dark mode stuff relating to unified search, fix macOS auto dark/light theme switching not always working, fix Windows detection of dark/light theme switching
Signed-off-by: Claudio Cambra <claudio.cambra@gmail.com>
This commit is contained in:
parent
1d66385119
commit
3d086ae305
|
@ -105,6 +105,31 @@ namespace {
|
|||
|
||||
// ----------------------------------------------------------------------------------
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
class WindowsNativeEventFilter : public QAbstractNativeEventFilter {
|
||||
public:
|
||||
bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) {
|
||||
const auto msg = static_cast<MSG *>(message);
|
||||
if(msg->message == WM_SYSCOLORCHANGE || msg->message == WM_SETTINGCHANGE) {
|
||||
if(!_guiAppInstance) {
|
||||
const auto ptr = qobject_cast<QGuiApplication *>(QGuiApplication::instance());
|
||||
if(ptr) {
|
||||
_guiAppInstance.reset(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
if(_guiAppInstance) {
|
||||
emit _guiAppInstance->paletteChanged(_guiAppInstance->palette());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
QScopedPointer<QGuiApplication> _guiAppInstance;
|
||||
};
|
||||
#endif
|
||||
|
||||
bool Application::configVersionMigration()
|
||||
{
|
||||
QStringList deleteKeys, ignoreKeys;
|
||||
|
@ -192,6 +217,9 @@ Application::Application(int &argc, char **argv)
|
|||
// Ensure OpenSSL config file is only loaded from app directory
|
||||
QString opensslConf = QCoreApplication::applicationDirPath() + QString("/openssl.cnf");
|
||||
qputenv("OPENSSL_CONF", opensslConf.toLocal8Bit());
|
||||
|
||||
// Set up event listener for Windows theme changing
|
||||
installNativeEventFilter(new WindowsNativeEventFilter());
|
||||
#endif
|
||||
|
||||
// TODO: Can't set this without breaking current config paths
|
||||
|
|
|
@ -130,12 +130,6 @@ Systray::Systray()
|
|||
});
|
||||
#endif
|
||||
|
||||
const auto ptr = qobject_cast<QGuiApplication *>(QGuiApplication::instance());
|
||||
if(ptr) {
|
||||
_guiAppInstance.reset(ptr);
|
||||
connect(ptr, &QGuiApplication::paletteChanged, this, &Systray::darkModeChanged);
|
||||
}
|
||||
|
||||
connect(UserModel::instance(), &UserModel::newUserSelected,
|
||||
this, &Systray::slotNewUserSelected);
|
||||
connect(UserModel::instance(), &UserModel::addAccount,
|
||||
|
@ -231,24 +225,6 @@ bool Systray::useNormalWindow() const
|
|||
return cfg.showMainDialogAsNormalWindow();
|
||||
}
|
||||
|
||||
bool Systray::darkMode()
|
||||
{
|
||||
#if defined(Q_OS_MACOS)
|
||||
return osXInDarkMode();
|
||||
// Windows: Check registry for dark mode
|
||||
#elif defined(Q_OS_WIN)
|
||||
const auto darkModeSubkey = QStringLiteral("Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize");
|
||||
if (!Utility::registryKeyExists(HKEY_CURRENT_USER, darkModeSubkey)) {
|
||||
return false;
|
||||
}
|
||||
const auto darkMode = !Utility::registryGetKeyValue(HKEY_CURRENT_USER, darkModeSubkey, QStringLiteral("AppsUseLightTheme")).toBool();
|
||||
return darkMode;
|
||||
// Probably Linux
|
||||
#else
|
||||
return Theme::isDarkColor(QGuiApplication::palette().window().color());
|
||||
#endif
|
||||
}
|
||||
|
||||
Q_INVOKABLE void Systray::setOpened()
|
||||
{
|
||||
_isOpen = true;
|
||||
|
|
|
@ -40,7 +40,6 @@ public:
|
|||
};
|
||||
|
||||
#ifdef Q_OS_OSX
|
||||
bool osXInDarkMode();
|
||||
bool canOsXSendUserNotification();
|
||||
void sendOsXUserNotification(const QString &title, const QString &message);
|
||||
void setTrayWindowLevelAndVisibleOnAllSpaces(QWindow *window);
|
||||
|
@ -58,7 +57,6 @@ class Systray
|
|||
|
||||
Q_PROPERTY(QString windowTitle READ windowTitle CONSTANT)
|
||||
Q_PROPERTY(bool useNormalWindow READ useNormalWindow CONSTANT)
|
||||
Q_PROPERTY(bool darkMode READ darkMode NOTIFY darkModeChanged)
|
||||
|
||||
public:
|
||||
static Systray *instance();
|
||||
|
@ -74,7 +72,6 @@ public:
|
|||
bool isOpen();
|
||||
QString windowTitle() const;
|
||||
bool useNormalWindow() const;
|
||||
bool darkMode();
|
||||
|
||||
Q_INVOKABLE void pauseResumeSync();
|
||||
Q_INVOKABLE bool syncIsPaused();
|
||||
|
@ -97,8 +94,6 @@ signals:
|
|||
void showFileActivityDialog(const QString &objectName, const int objectId);
|
||||
void sendChatMessage(const QString &token, const QString &message, const QString &replyTo);
|
||||
|
||||
void darkModeChanged();
|
||||
|
||||
public slots:
|
||||
void slotNewUserSelected();
|
||||
|
||||
|
|
|
@ -77,11 +77,7 @@ RowLayout {
|
|||
anchors.bottom: if(model.thumbnail !== undefined) parent.bottom
|
||||
cache: true
|
||||
|
||||
property string sourceUrl: Systray.darkMode ?
|
||||
model.icon.replace("__COLOR__", "white").replace("__WHITE_GOES_HERE__", "-white") :
|
||||
model.icon.replace("__COLOR__", "black").replace("__WHITE_GOES_HERE__", "")
|
||||
|
||||
source: sourceUrl
|
||||
source: Theme.darkMode ? model.darkIcon : model.lightIcon
|
||||
sourceSize.height: 64
|
||||
sourceSize.width: 64
|
||||
}
|
||||
|
@ -146,9 +142,9 @@ RowLayout {
|
|||
id: talkReplyMessage
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: dismissActionButton
|
||||
|
||||
|
@ -184,7 +180,7 @@ RowLayout {
|
|||
|
||||
contentItem: Image {
|
||||
anchors.fill: parent
|
||||
source: parent.hovered ? Systray.darkMode ?
|
||||
source: parent.hovered ? Theme.darkMode ?
|
||||
"image://svgimage-custom-color/clear.svg/white" : "image://svgimage-custom-color/clear.svg/black" :
|
||||
"image://svgimage-custom-color/clear.svg/grey"
|
||||
sourceSize.width: 24
|
||||
|
|
|
@ -2,6 +2,7 @@ import QtQml 2.15
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.3
|
||||
import Style 1.0
|
||||
import com.nextcloud.desktopclient 1.0
|
||||
|
||||
MouseArea {
|
||||
id: unifiedSearchResultMouseArea
|
||||
|
@ -60,8 +61,8 @@ MouseArea {
|
|||
height: unifiedSearchResultMouseArea.height
|
||||
title: model.resultTitle
|
||||
subline: model.subline
|
||||
icons: model.icons
|
||||
iconPlaceholder: model.imagePlaceholder
|
||||
icons: Theme.darkMode ? model.darkIcons : model.lightIcons
|
||||
iconPlaceholder: Theme.darkMode ? model.darkImagePlaceholder : model.lightImagePlaceholder
|
||||
isRounded: model.isRounded
|
||||
textLeftMargin: unifiedSearchResultMouseArea.textLeftMargin
|
||||
textRightMargin: unifiedSearchResultMouseArea.textRightMargin
|
||||
|
|
|
@ -61,7 +61,7 @@ MenuItem {
|
|||
Layout.leftMargin: 7
|
||||
verticalAlignment: Qt.AlignCenter
|
||||
cache: false
|
||||
source: model.avatar != "" ? model.avatar : Systray.darkMode ? "image://avatars/fallbackWhite" : "image://avatars/fallbackBlack"
|
||||
source: model.avatar != "" ? model.avatar : Theme.darkMode ? "image://avatars/fallbackWhite" : "image://avatars/fallbackBlack"
|
||||
Layout.preferredHeight: Style.accountAvatarSize
|
||||
Layout.preferredWidth: Style.accountAvatarSize
|
||||
Rectangle {
|
||||
|
|
|
@ -239,7 +239,7 @@ Window {
|
|||
Image {
|
||||
Layout.leftMargin: 12
|
||||
verticalAlignment: Qt.AlignCenter
|
||||
source: Systray.darkMode ? "qrc:///client/theme/white/add.svg" : "qrc:///client/theme/black/add.svg"
|
||||
source: Theme.darkMode ? "qrc:///client/theme/white/add.svg" : "qrc:///client/theme/black/add.svg"
|
||||
sourceSize.width: Style.headerButtonIconSize
|
||||
sourceSize.height: Style.headerButtonIconSize
|
||||
}
|
||||
|
|
|
@ -63,9 +63,25 @@ OCC::Activity Activity::fromActivityJson(const QJsonObject json, const AccountPt
|
|||
activity._file = json.value(QStringLiteral("object_name")).toString();
|
||||
activity._link = QUrl(json.value(QStringLiteral("link")).toString());
|
||||
activity._dateTime = QDateTime::fromString(json.value(QStringLiteral("datetime")).toString(), Qt::ISODate);
|
||||
activity._icon = json.value(QStringLiteral("icon")).toString();
|
||||
activity._darkIcon = json.value(QStringLiteral("icon")).toString(); // We have both dark and light for theming purposes
|
||||
activity._lightIcon = json.value(QStringLiteral("icon")).toString(); // Some icons get changed in the ActivityListModel
|
||||
activity._isCurrentUserFileActivity = activity._objectType == QStringLiteral("files") && activityUser == account->davUser();
|
||||
|
||||
const auto darkIconPath = QStringLiteral("qrc://:/client/theme/white/");
|
||||
const auto lightIconPath = QStringLiteral("qrc://:/client/theme/black/");
|
||||
if(activity._darkIcon.contains("change.svg")) {
|
||||
activity._darkIcon = darkIconPath + QStringLiteral("change.svg");
|
||||
activity._lightIcon = lightIconPath + QStringLiteral("change.svg");
|
||||
} else if(activity._darkIcon.contains("calendar.svg")) {
|
||||
activity._darkIcon = darkIconPath + QStringLiteral("calendar.svg");
|
||||
activity._lightIcon = lightIconPath + QStringLiteral("calendar.svg");
|
||||
} else if(activity._darkIcon.contains("personal.svg")) {
|
||||
activity._darkIcon = darkIconPath + QStringLiteral("user.svg");
|
||||
activity._lightIcon = lightIconPath + QStringLiteral("user.svg");
|
||||
} else if(activity._darkIcon.contains("core/img/actions")) {
|
||||
activity._darkIcon.insert(activity._darkIcon.indexOf(".svg"), "-white");
|
||||
}
|
||||
|
||||
auto richSubjectData = json.value(QStringLiteral("subject_rich")).toArray();
|
||||
|
||||
if(richSubjectData.size() > 1) {
|
||||
|
@ -128,15 +144,23 @@ OCC::Activity Activity::fromActivityJson(const QJsonObject json, const AccountPt
|
|||
}
|
||||
|
||||
if(!previewsData.isEmpty()) {
|
||||
if(activity._icon.contains(QStringLiteral("add-color.svg"))) {
|
||||
activity._icon = "qrc:///client/theme/colored/add-bordered.svg";
|
||||
} else if(activity._icon.contains(QStringLiteral("delete-color.svg"))) {
|
||||
activity._icon = "qrc:///client/theme/colored/delete-bordered.svg";
|
||||
} else if(activity._icon.contains(QStringLiteral("change.svg"))) {
|
||||
activity._icon = "qrc:///client/theme/colored/change-bordered.svg";
|
||||
if(activity._darkIcon.contains(QStringLiteral("add-color.svg"))) {
|
||||
activity._darkIcon = "qrc:///client/theme/colored/add-bordered.svg";
|
||||
activity._lightIcon = "qrc:///client/theme/colored/add-bordered.svg";
|
||||
} else if(activity._darkIcon.contains(QStringLiteral("delete-color.svg"))) {
|
||||
activity._darkIcon = "qrc:///client/theme/colored/delete-bordered.svg";
|
||||
activity._lightIcon = "qrc:///client/theme/colored/add-bordered.svg";
|
||||
} else if(activity._darkIcon.contains(QStringLiteral("change.svg"))) {
|
||||
activity._darkIcon = "qrc:///client/theme/colored/change-bordered.svg";
|
||||
activity._lightIcon = "qrc:///client/theme/colored/add-bordered.svg";
|
||||
}
|
||||
}
|
||||
|
||||
auto actions = json.value("actions").toArray();
|
||||
foreach (auto action, actions) {
|
||||
activity._links.append(ActivityLink::createFomJsonObject(action.toObject()));
|
||||
}
|
||||
|
||||
return activity;
|
||||
}
|
||||
|
||||
|
|
|
@ -133,7 +133,8 @@ public:
|
|||
QDateTime _dateTime;
|
||||
qint64 _expireAtMsecs = -1;
|
||||
QString _accName;
|
||||
QString _icon;
|
||||
QString _darkIcon;
|
||||
QString _lightIcon;
|
||||
bool _isCurrentUserFileActivity = false;
|
||||
QVector<PreviewData> _previews;
|
||||
|
||||
|
|
|
@ -62,7 +62,8 @@ QHash<int, QByteArray> ActivityListModel::roleNames() const
|
|||
roles[LinkRole] = "link";
|
||||
roles[MessageRole] = "message";
|
||||
roles[ActionRole] = "type";
|
||||
roles[ActionIconRole] = "icon";
|
||||
roles[DarkIconRole] = "darkIcon";
|
||||
roles[LightIconRole] = "lightIcon";
|
||||
roles[ActionTextRole] = "subject";
|
||||
roles[ActionsLinksRole] = "links";
|
||||
roles[ActionsLinksContextMenuRole] = "linksContextMenu";
|
||||
|
@ -192,31 +193,8 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
|
|||
});
|
||||
};
|
||||
|
||||
switch (role) {
|
||||
case DisplayPathRole:
|
||||
return getDisplayPath();
|
||||
case PathRole:
|
||||
return QFileInfo(getFilePath()).path();
|
||||
case DisplayLocationRole:
|
||||
return displayLocation();
|
||||
case ActionsLinksRole: {
|
||||
QList<QVariant> customList;
|
||||
foreach (ActivityLink activityLink, a._links) {
|
||||
customList << QVariant::fromValue(activityLink);
|
||||
}
|
||||
return customList;
|
||||
}
|
||||
|
||||
case ActionsLinksContextMenuRole: {
|
||||
return ActivityListModel::convertLinksToMenuEntries(a);
|
||||
}
|
||||
|
||||
case ActionsLinksForActionButtonsRole: {
|
||||
return ActivityListModel::convertLinksToActionButtons(a);
|
||||
}
|
||||
|
||||
case ActionIconRole: {
|
||||
auto colorIconPath = QStringLiteral("qrc:///client/theme/__COLOR__/"); // We will replace __COLOR__ in QML
|
||||
const auto generateIconPath = [&]() {
|
||||
auto colorIconPath = role == DarkIconRole ? QStringLiteral("qrc:///client/theme/white/") : QStringLiteral("qrc:///client/theme/black/");
|
||||
if (a._type == Activity::NotificationType) {
|
||||
colorIconPath.append("bell.svg");
|
||||
return colorIconPath;
|
||||
|
@ -255,14 +233,40 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
|
|||
}
|
||||
} else {
|
||||
// We have an activity
|
||||
if (a._icon.isEmpty()) {
|
||||
if (a._darkIcon.isEmpty()) {
|
||||
colorIconPath.append("activity.svg");
|
||||
return colorIconPath;
|
||||
}
|
||||
|
||||
return a._icon;
|
||||
return role == DarkIconRole ? a._darkIcon : a._lightIcon;
|
||||
}
|
||||
};
|
||||
|
||||
switch (role) {
|
||||
case DisplayPathRole:
|
||||
return getDisplayPath();
|
||||
case PathRole:
|
||||
return QFileInfo(getFilePath()).path();
|
||||
case DisplayLocationRole:
|
||||
return displayLocation();
|
||||
case ActionsLinksRole: {
|
||||
QList<QVariant> customList;
|
||||
foreach (ActivityLink activityLink, a._links) {
|
||||
customList << QVariant::fromValue(activityLink);
|
||||
}
|
||||
return customList;
|
||||
}
|
||||
|
||||
case ActionsLinksContextMenuRole: {
|
||||
return ActivityListModel::convertLinksToMenuEntries(a);
|
||||
}
|
||||
|
||||
case ActionsLinksForActionButtonsRole: {
|
||||
return ActivityListModel::convertLinksToActionButtons(a);
|
||||
}
|
||||
|
||||
case DarkIconRole:
|
||||
case LightIconRole:
|
||||
return generateIconPath();
|
||||
case ObjectTypeRole:
|
||||
return a._objectType;
|
||||
case ObjectIdRole:
|
||||
|
@ -400,20 +404,6 @@ void ActivityListModel::ingestActivities(const QJsonArray &activities)
|
|||
|
||||
auto a = Activity::fromActivityJson(json, _accountState->account());
|
||||
|
||||
auto colorIconPath = QStringLiteral("qrc:///client/theme/__COLOR__/");
|
||||
if(a._icon.contains("change.svg")) {
|
||||
colorIconPath.append("change.svg");
|
||||
a._icon = colorIconPath;
|
||||
} else if(a._icon.contains("calendar.svg")) {
|
||||
colorIconPath.append("calendar.svg");
|
||||
a._icon = colorIconPath;
|
||||
} else if(a._icon.contains("personal.svg")) {
|
||||
colorIconPath.append("user.svg");
|
||||
a._icon = colorIconPath;
|
||||
} else if(a._icon.contains("core/img/actions")) {
|
||||
a._icon.insert(a._icon.indexOf(".svg"), "__WHITE_GOES_HERE__");
|
||||
}
|
||||
|
||||
list.append(a);
|
||||
_currentItem = list.last()._id;
|
||||
|
||||
|
|
|
@ -45,7 +45,8 @@ class ActivityListModel : public QAbstractListModel
|
|||
Q_PROPERTY(AccountState *accountState READ accountState CONSTANT)
|
||||
public:
|
||||
enum DataRole {
|
||||
ActionIconRole = Qt::UserRole + 1,
|
||||
DarkIconRole = Qt::UserRole + 1,
|
||||
LightIconRole,
|
||||
AccountRole,
|
||||
ObjectTypeRole,
|
||||
ObjectIdRole,
|
||||
|
|
|
@ -92,15 +92,11 @@ void ServerNotificationHandler::slotNotificationsReceived(const QJsonDocument &j
|
|||
ActivityList list;
|
||||
|
||||
foreach (auto element, notifies) {
|
||||
Activity a;
|
||||
auto json = element.toObject();
|
||||
auto a = Activity::fromActivityJson(json, ai->account());
|
||||
a._type = Activity::NotificationType;
|
||||
a._accName = ai->account()->displayName();
|
||||
a._id = json.value("notification_id").toInt();
|
||||
|
||||
//need to know, specially for remote_share
|
||||
a._objectType = json.value("object_type").toString();
|
||||
|
||||
// 2 cases to consider:
|
||||
// - server == 24 & has Talk: notification type chat/call contains conversationToken/messageId in object_type
|
||||
// - server < 24 & has Talk: notification type chat/call contains _only_ the conversationToken in object_type
|
||||
|
@ -117,10 +113,6 @@ void ServerNotificationHandler::slotNotificationsReceived(const QJsonDocument &j
|
|||
|
||||
a._status = 0;
|
||||
|
||||
a._subject = json.value("subject").toString();
|
||||
a._message = json.value("message").toString();
|
||||
a._icon = json.value("icon").toString();
|
||||
|
||||
QUrl link(json.value("link").toString());
|
||||
if (!link.isEmpty()) {
|
||||
if (link.host().isEmpty()) {
|
||||
|
@ -132,12 +124,6 @@ void ServerNotificationHandler::slotNotificationsReceived(const QJsonDocument &j
|
|||
}
|
||||
}
|
||||
a._link = link;
|
||||
a._dateTime = QDateTime::fromString(json.value("datetime").toString(), Qt::ISODate);
|
||||
|
||||
auto actions = json.value("actions").toArray();
|
||||
foreach (auto action, actions) {
|
||||
a._links.append(ActivityLink::createFomJsonObject(action.toObject()));
|
||||
}
|
||||
|
||||
// Add another action to dismiss notification on server
|
||||
// https://github.com/owncloud/notifications/blob/master/docs/ocs-endpoint-v1.md#deleting-a-notification-for-a-user
|
||||
|
|
|
@ -42,7 +42,8 @@ struct UnifiedSearchResult
|
|||
bool _isRounded = false;
|
||||
qint32 _order = std::numeric_limits<qint32>::max();
|
||||
QUrl _resourceUrl;
|
||||
QString _icons;
|
||||
QString _darkIcons;
|
||||
QString _lightIcons;
|
||||
Type _type = Type::Default;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include "guiutility.h"
|
||||
#include "folderman.h"
|
||||
#include "networkjobs.h"
|
||||
#include "systray.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
@ -27,47 +26,49 @@
|
|||
#include <QDesktopServices>
|
||||
|
||||
namespace {
|
||||
QString imagePlaceholderUrlForProviderId(const QString &providerId)
|
||||
QString imagePlaceholderUrlForProviderId(const QString &providerId, const bool darkMode)
|
||||
{
|
||||
const auto colorIconPath = darkMode ? QStringLiteral(":/client/theme/white/") : QStringLiteral(":/client/theme/black/");
|
||||
if (providerId.contains(QStringLiteral("message"), Qt::CaseInsensitive)
|
||||
|| providerId.contains(QStringLiteral("talk"), Qt::CaseInsensitive)) {
|
||||
return OCC::Systray::instance()->darkMode() ? QStringLiteral("qrc:///client/theme/white/wizard-talk.svg") : QStringLiteral("qrc:///client/theme/black/wizard-talk.svg");
|
||||
return colorIconPath % QStringLiteral("wizard-talk.svg");
|
||||
} else if (providerId.contains(QStringLiteral("file"), Qt::CaseInsensitive)) {
|
||||
return OCC::Systray::instance()->darkMode() ? QStringLiteral("qrc:///client/theme/white/edit.svg") : QStringLiteral("qrc:///client/theme/black/edit.svg");
|
||||
return colorIconPath % QStringLiteral("edit.svg");
|
||||
} else if (providerId.contains(QStringLiteral("deck"), Qt::CaseInsensitive)) {
|
||||
return OCC::Systray::instance()->darkMode() ? QStringLiteral("qrc:///client/theme/white/deck.svg") : QStringLiteral("qrc:///client/theme/black/deck.svg");
|
||||
return colorIconPath % QStringLiteral("deck.svg");
|
||||
} else if (providerId.contains(QStringLiteral("calendar"), Qt::CaseInsensitive)) {
|
||||
return OCC::Systray::instance()->darkMode() ? QStringLiteral("qrc:///client/theme/white/calendar.svg") : QStringLiteral("qrc:///client/theme/black/calendar.svg");
|
||||
return colorIconPath % QStringLiteral("calendar.svg");
|
||||
} else if (providerId.contains(QStringLiteral("mail"), Qt::CaseInsensitive)) {
|
||||
return OCC::Systray::instance()->darkMode() ? QStringLiteral("qrc:///client/theme/white/email.svg") : QStringLiteral("qrc:///client/theme/black/email.svg");
|
||||
return colorIconPath % QStringLiteral("email.svg");
|
||||
} else if (providerId.contains(QStringLiteral("comment"), Qt::CaseInsensitive)) {
|
||||
return OCC::Systray::instance()->darkMode() ? QStringLiteral("qrc:///client/theme/white/comment.svg") : QStringLiteral("qrc:///client/theme/black/comment.svg");
|
||||
return colorIconPath % QStringLiteral("comment.svg");
|
||||
}
|
||||
|
||||
return QStringLiteral("qrc:///client/theme/change.svg");
|
||||
return colorIconPath % QStringLiteral("change.svg");
|
||||
}
|
||||
|
||||
QString localIconPathFromIconPrefix(const QString &iconNameWithPrefix)
|
||||
QString localIconPathFromIconPrefix(const QString &iconNameWithPrefix, const bool darkMode)
|
||||
{
|
||||
const auto colorIconPath = darkMode ? QStringLiteral(":/client/theme/white/") : QStringLiteral(":/client/theme/black/");
|
||||
if (iconNameWithPrefix.contains(QStringLiteral("message"), Qt::CaseInsensitive)
|
||||
|| iconNameWithPrefix.contains(QStringLiteral("talk"), Qt::CaseInsensitive)) {
|
||||
return OCC::Systray::instance()->darkMode() ? QStringLiteral(":/client/theme/white/wizard-talk.svg") : QStringLiteral(":/client/theme/black/wizard-talk.svg");
|
||||
return colorIconPath % QStringLiteral("wizard-talk.svg");
|
||||
} else if (iconNameWithPrefix.contains(QStringLiteral("folder"), Qt::CaseInsensitive)) {
|
||||
return OCC::Systray::instance()->darkMode() ? QStringLiteral(":/client/theme/white/folder.svg") : QStringLiteral(":/client/theme/black/folder.svg");
|
||||
return colorIconPath % QStringLiteral("folder.svg");
|
||||
} else if (iconNameWithPrefix.contains(QStringLiteral("deck"), Qt::CaseInsensitive)) {
|
||||
return OCC::Systray::instance()->darkMode() ? QStringLiteral(":/client/theme/white/deck.svg") : QStringLiteral(":/client/theme/black/deck.svg");
|
||||
return colorIconPath % QStringLiteral("deck.svg");
|
||||
} else if (iconNameWithPrefix.contains(QStringLiteral("contacts"), Qt::CaseInsensitive)) {
|
||||
return OCC::Systray::instance()->darkMode() ? QStringLiteral(":/client/theme/white/wizard-groupware.svg") : QStringLiteral(":/client/theme/black/wizard-groupware.svg");
|
||||
return colorIconPath % QStringLiteral("wizard-groupware.svg");
|
||||
} else if (iconNameWithPrefix.contains(QStringLiteral("calendar"), Qt::CaseInsensitive)) {
|
||||
return OCC::Systray::instance()->darkMode() ? QStringLiteral(":/client/theme/white/calendar.svg") : QStringLiteral(":/client/theme/black/calendar.svg");
|
||||
return colorIconPath % QStringLiteral("calendar.svg");
|
||||
} else if (iconNameWithPrefix.contains(QStringLiteral("mail"), Qt::CaseInsensitive)) {
|
||||
return OCC::Systray::instance()->darkMode() ? QStringLiteral(":/client/theme/white/email.svg") : QStringLiteral(":/client/theme/black/email.svg");
|
||||
return colorIconPath % QStringLiteral("email.svg");
|
||||
}
|
||||
|
||||
return OCC::Systray::instance()->darkMode() ? QStringLiteral(":/client/theme/white/change.svg") : QStringLiteral(":/client/theme/change.svg");
|
||||
return colorIconPath % QStringLiteral("change.svg");
|
||||
}
|
||||
|
||||
QString iconUrlForDefaultIconName(const QString &defaultIconName)
|
||||
QString iconUrlForDefaultIconName(const QString &defaultIconName, const bool darkMode)
|
||||
{
|
||||
const QUrl urlForIcon{defaultIconName};
|
||||
|
||||
|
@ -75,15 +76,16 @@ QString iconUrlForDefaultIconName(const QString &defaultIconName)
|
|||
return defaultIconName;
|
||||
}
|
||||
|
||||
const auto colorIconPath = darkMode ? QStringLiteral(":/client/theme/white/") : QStringLiteral(":/client/theme/black/");
|
||||
|
||||
if (defaultIconName.startsWith(QStringLiteral("icon-"))) {
|
||||
const auto parts = defaultIconName.split(QLatin1Char('-'));
|
||||
|
||||
if (parts.size() > 1) {
|
||||
const QString blackOrWhite = OCC::Systray::instance()->darkMode() ? QStringLiteral(":/client/theme/white/") : QStringLiteral(":/client/theme/black/");
|
||||
const QString blackIconFilePath = blackOrWhite + parts[1] + QStringLiteral(".svg");
|
||||
const QString blackOrWhiteIconFilePath = colorIconPath + parts[1] + QStringLiteral(".svg");
|
||||
|
||||
if (QFile::exists(blackIconFilePath)) {
|
||||
return blackIconFilePath;
|
||||
if (QFile::exists(blackOrWhiteIconFilePath)) {
|
||||
return blackOrWhiteIconFilePath;
|
||||
}
|
||||
|
||||
const QString iconFilePath = QStringLiteral(":/client/theme/") + parts[1] + QStringLiteral(".svg");
|
||||
|
@ -93,14 +95,14 @@ QString iconUrlForDefaultIconName(const QString &defaultIconName)
|
|||
}
|
||||
}
|
||||
|
||||
const auto iconNameFromIconPrefix = localIconPathFromIconPrefix(defaultIconName);
|
||||
const auto iconNameFromIconPrefix = localIconPathFromIconPrefix(defaultIconName, darkMode);
|
||||
|
||||
if (!iconNameFromIconPrefix.isEmpty()) {
|
||||
return iconNameFromIconPrefix;
|
||||
}
|
||||
}
|
||||
|
||||
return OCC::Systray::instance()->darkMode() ? QStringLiteral(":/client/theme/white/change.svg") : QStringLiteral(":/client/theme/change.svg");
|
||||
return colorIconPath % QStringLiteral("change.svg");
|
||||
}
|
||||
|
||||
QString generateUrlForThumbnail(const QString &thumbnailUrl, const QUrl &serverUrl)
|
||||
|
@ -125,7 +127,7 @@ QString generateUrlForThumbnail(const QString &thumbnailUrl, const QUrl &serverU
|
|||
return thumbnailUrlCopy;
|
||||
}
|
||||
|
||||
QString generateUrlForIcon(const QString &fallbackIcon, const QUrl &serverUrl)
|
||||
QString generateUrlForIcon(const QString &fallbackIcon, const QUrl &serverUrl, const bool darkMode)
|
||||
{
|
||||
auto serverUrlCopy = serverUrl;
|
||||
|
||||
|
@ -144,7 +146,7 @@ QString generateUrlForIcon(const QString &fallbackIcon, const QUrl &serverUrl)
|
|||
}
|
||||
} else if (!fallbackIconCopy.isEmpty()) {
|
||||
// could be one of names for standard icons (e.g. icon-mail)
|
||||
const auto defaultIconUrl = iconUrlForDefaultIconName(fallbackIconCopy);
|
||||
const auto defaultIconUrl = iconUrlForDefaultIconName(fallbackIconCopy, darkMode);
|
||||
if (!defaultIconUrl.isEmpty()) {
|
||||
fallbackIconCopy = defaultIconUrl;
|
||||
}
|
||||
|
@ -153,7 +155,7 @@ QString generateUrlForIcon(const QString &fallbackIcon, const QUrl &serverUrl)
|
|||
return fallbackIconCopy;
|
||||
}
|
||||
|
||||
QString iconsFromThumbnailAndFallbackIcon(const QString &thumbnailUrl, const QString &fallbackIcon, const QUrl &serverUrl)
|
||||
QString iconsFromThumbnailAndFallbackIcon(const QString &thumbnailUrl, const QString &fallbackIcon, const QUrl &serverUrl, const bool darkMode)
|
||||
{
|
||||
if (thumbnailUrl.isEmpty() && fallbackIcon.isEmpty()) {
|
||||
return {};
|
||||
|
@ -165,7 +167,7 @@ QString iconsFromThumbnailAndFallbackIcon(const QString &thumbnailUrl, const QSt
|
|||
}
|
||||
|
||||
const auto urlForThumbnail = generateUrlForThumbnail(thumbnailUrl, serverUrl);
|
||||
const auto urlForFallbackIcon = generateUrlForIcon(fallbackIcon, serverUrl);
|
||||
const auto urlForFallbackIcon = generateUrlForIcon(fallbackIcon, serverUrl, darkMode);
|
||||
|
||||
qDebug() << "SEARCH" << urlForThumbnail << urlForFallbackIcon;
|
||||
|
||||
|
@ -204,10 +206,14 @@ QVariant UnifiedSearchResultsListModel::data(const QModelIndex &index, int role)
|
|||
return _results.at(index.row())._providerName;
|
||||
case ProviderIdRole:
|
||||
return _results.at(index.row())._providerId;
|
||||
case ImagePlaceholderRole:
|
||||
return imagePlaceholderUrlForProviderId(_results.at(index.row())._providerId);
|
||||
case IconsRole:
|
||||
return _results.at(index.row())._icons;
|
||||
case DarkImagePlaceholderRole:
|
||||
return imagePlaceholderUrlForProviderId(_results.at(index.row())._providerId, true);
|
||||
case LightImagePlaceholderRole:
|
||||
return imagePlaceholderUrlForProviderId(_results.at(index.row())._providerId, false);
|
||||
case DarkIconsRole:
|
||||
return _results.at(index.row())._darkIcons;
|
||||
case LightIconsRole:
|
||||
return _results.at(index.row())._lightIcons;
|
||||
case TitleRole:
|
||||
return _results.at(index.row())._title;
|
||||
case SublineRole:
|
||||
|
@ -239,8 +245,10 @@ QHash<int, QByteArray> UnifiedSearchResultsListModel::roleNames() const
|
|||
auto roles = QAbstractListModel::roleNames();
|
||||
roles[ProviderNameRole] = "providerName";
|
||||
roles[ProviderIdRole] = "providerId";
|
||||
roles[IconsRole] = "icons";
|
||||
roles[ImagePlaceholderRole] = "imagePlaceholder";
|
||||
roles[DarkIconsRole] = "darkIcons";
|
||||
roles[LightIconsRole] = "lightIcons";
|
||||
roles[DarkImagePlaceholderRole] = "darkImagePlaceholder";
|
||||
roles[LightImagePlaceholderRole] = "lightImagePlaceholder";
|
||||
roles[TitleRole] = "resultTitle";
|
||||
roles[SublineRole] = "subline";
|
||||
roles[ResourceUrlRole] = "resourceUrlRole";
|
||||
|
@ -577,8 +585,10 @@ void UnifiedSearchResultsListModel::parseResultsForProvider(const QJsonObject &d
|
|||
const auto accountUrl = (_accountState && _accountState->account()) ? _accountState->account()->url() : QUrl();
|
||||
|
||||
result._resourceUrl = makeResourceUrl(resourceUrl, accountUrl);
|
||||
result._icons = iconsFromThumbnailAndFallbackIcon(entryMap.value(QStringLiteral("thumbnailUrl")).toString(),
|
||||
entryMap.value(QStringLiteral("icon")).toString(), accountUrl);
|
||||
result._darkIcons = iconsFromThumbnailAndFallbackIcon(entryMap.value(QStringLiteral("thumbnailUrl")).toString(),
|
||||
entryMap.value(QStringLiteral("icon")).toString(), accountUrl, true);
|
||||
result._lightIcons = iconsFromThumbnailAndFallbackIcon(entryMap.value(QStringLiteral("thumbnailUrl")).toString(),
|
||||
entryMap.value(QStringLiteral("icon")).toString(), accountUrl, false);
|
||||
|
||||
newEntries.push_back(result);
|
||||
}
|
||||
|
|
|
@ -53,8 +53,10 @@ public:
|
|||
enum DataRole {
|
||||
ProviderNameRole = Qt::UserRole + 1,
|
||||
ProviderIdRole,
|
||||
ImagePlaceholderRole,
|
||||
IconsRole,
|
||||
DarkImagePlaceholderRole,
|
||||
LightImagePlaceholderRole,
|
||||
DarkIconsRole,
|
||||
LightIconsRole,
|
||||
TitleRole,
|
||||
SublineRole,
|
||||
ResourceUrlRole,
|
||||
|
|
|
@ -343,6 +343,9 @@ QString Theme::hidpiFileName(const QString &iconName, const QColor &backgroundCo
|
|||
Theme::Theme()
|
||||
: QObject(nullptr)
|
||||
{
|
||||
#if defined(Q_OS_WIN)
|
||||
reserveDarkPalette = QPalette(QColor(49,49,49,255), QColor(35,35,35,255)); // Windows 11 button and window dark colours
|
||||
#endif
|
||||
}
|
||||
|
||||
// If this option returns true, the client only supports one folder to sync.
|
||||
|
@ -899,16 +902,44 @@ QColor Theme::errorBoxBorderColor() const
|
|||
return QColor{"black"};
|
||||
}
|
||||
|
||||
QPalette Theme::systemPalette()
|
||||
void Theme::connectToPaletteSignal()
|
||||
{
|
||||
if(!_guiAppInstance) {
|
||||
const auto ptr = qobject_cast<QGuiApplication *>(QGuiApplication::instance());
|
||||
if(ptr) {
|
||||
_guiAppInstance.reset(ptr);
|
||||
connect(ptr, &QGuiApplication::paletteChanged, this, &Theme::systemPaletteChanged);
|
||||
connect(_guiAppInstance.data(), &QGuiApplication::paletteChanged, this, &Theme::systemPaletteChanged);
|
||||
connect(_guiAppInstance.data(), &QGuiApplication::paletteChanged, this, &Theme::darkModeChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QPalette Theme::systemPalette()
|
||||
{
|
||||
connectToPaletteSignal();
|
||||
#if defined(Q_OS_WIN)
|
||||
if(darkMode()) {
|
||||
return reserveDarkPalette;
|
||||
}
|
||||
#endif
|
||||
return QGuiApplication::palette();
|
||||
}
|
||||
|
||||
bool Theme::darkMode()
|
||||
{
|
||||
connectToPaletteSignal();
|
||||
// Windows: Check registry for dark mode
|
||||
#if defined(Q_OS_WIN)
|
||||
const auto darkModeSubkey = QStringLiteral("Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize");
|
||||
if (Utility::registryKeyExists(HKEY_CURRENT_USER, darkModeSubkey) &&
|
||||
!Utility::registryGetKeyValue(HKEY_CURRENT_USER, darkModeSubkey, QStringLiteral("AppsUseLightTheme")).toBool()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
#else
|
||||
return Theme::isDarkColor(QGuiApplication::palette().window().color());
|
||||
#endif
|
||||
}
|
||||
|
||||
} // end namespace client
|
||||
|
|
|
@ -69,6 +69,7 @@ class OWNCLOUDSYNC_EXPORT Theme : public QObject
|
|||
Q_PROPERTY(QColor errorBoxBorderColor READ errorBoxBorderColor CONSTANT)
|
||||
|
||||
Q_PROPERTY(QPalette systemPalette READ systemPalette NOTIFY systemPaletteChanged)
|
||||
Q_PROPERTY(bool darkMode READ darkMode NOTIFY darkModeChanged)
|
||||
public:
|
||||
enum CustomMediaType {
|
||||
oCSetupTop, // ownCloud connect page
|
||||
|
@ -594,6 +595,7 @@ public:
|
|||
static constexpr const char *themePrefix = ":/client/theme/";
|
||||
|
||||
QPalette systemPalette();
|
||||
bool darkMode();
|
||||
|
||||
protected:
|
||||
#ifndef TOKEN_AUTH_ONLY
|
||||
|
@ -612,14 +614,21 @@ protected:
|
|||
signals:
|
||||
void systrayUseMonoIconsChanged(bool);
|
||||
void systemPaletteChanged(const QPalette &palette);
|
||||
void darkModeChanged();
|
||||
|
||||
private:
|
||||
Theme(Theme const &);
|
||||
Theme &operator=(Theme const &);
|
||||
|
||||
void connectToPaletteSignal();
|
||||
#if defined(Q_OS_WIN)
|
||||
QPalette reserveDarkPalette; // Windows 11 button and window dark colours
|
||||
#endif
|
||||
|
||||
static Theme *_instance;
|
||||
bool _mono = false;
|
||||
QScopedPointer<QGuiApplication> _guiAppInstance;
|
||||
|
||||
#ifndef TOKEN_AUTH_ONLY
|
||||
mutable QHash<QString, QIcon> _iconCache;
|
||||
#endif
|
||||
|
|
|
@ -598,7 +598,8 @@ private slots:
|
|||
|
||||
QVERIFY(!index.data(OCC::ActivityListModel::AccountRole).toString().isEmpty());
|
||||
QVERIFY(!index.data(OCC::ActivityListModel::ActionTextColorRole).toString().isEmpty());
|
||||
QVERIFY(!index.data(OCC::ActivityListModel::ActionIconRole).toString().isEmpty());
|
||||
QVERIFY(!index.data(OCC::ActivityListModel::DarkIconRole).toString().isEmpty());
|
||||
QVERIFY(!index.data(OCC::ActivityListModel::LightIconRole).toString().isEmpty());
|
||||
QVERIFY(!index.data(OCC::ActivityListModel::PointInTimeRole).toString().isEmpty());
|
||||
|
||||
QVERIFY(index.data(OCC::ActivityListModel::ObjectTypeRole).canConvert<int>());
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
<file>theme/white/state-sync-64.png</file>
|
||||
<file>theme/white/state-sync-128.png</file>
|
||||
<file>theme/white/state-sync-256.png</file>
|
||||
<file>theme/black/change.svg</file>
|
||||
<file>theme/black/clear.svg</file>
|
||||
<file>theme/black/comment.svg</file>
|
||||
<file>theme/black/search.svg</file>
|
||||
|
@ -90,6 +91,7 @@
|
|||
<file>theme/white/folder@2x.png</file>
|
||||
<file>theme/colored/folder.png</file>
|
||||
<file>theme/colored/folder@2x.png</file>
|
||||
<file>theme/black/confirm.svg</file>
|
||||
<file>theme/black/control-next.svg</file>
|
||||
<file>theme/black/control-prev.svg</file>
|
||||
<file>theme/black/settings.svg</file>
|
||||
|
|
|
@ -12,8 +12,8 @@ QtObject {
|
|||
readonly property color ncTextColor: Theme.systemPalette.windowText
|
||||
readonly property color ncSecondaryTextColor: "#808080"
|
||||
readonly property color ncHeaderTextColor: "white"
|
||||
readonly property color lightHover: Systray.darkMode ? Qt.lighter(backgroundColor, 2) : Qt.darker(backgroundColor, 1.05)
|
||||
readonly property color menuBorder: Systray.darkMode ? Qt.lighter(backgroundColor, 3) : Qt.darker(backgroundColor, 1.5)
|
||||
readonly property color lightHover: Theme.darkMode ? Qt.lighter(backgroundColor, 2) : Qt.darker(backgroundColor, 1.05)
|
||||
readonly property color menuBorder: ncSecondaryTextColor
|
||||
readonly property color backgroundColor: Theme.systemPalette.base
|
||||
|
||||
// ErrorBox colors
|
||||
|
|
Loading…
Reference in New Issue