Parcourir la source

Add support for server color theming by using server color as accent
color

Signed-off-by: Claudio Cambra <claudio.cambra@gmail.com>

Claudio Cambra il y a 4 ans
Parent
commit
f585b8bd48

+ 1 - 0
src/gui/main.cpp

@@ -69,6 +69,7 @@ int main(int argc, char **argv)
     qmlRegisterType<UserStatusSelectorModel>("com.nextcloud.desktopclient", 1, 0, "UserStatusSelectorModel");
     qmlRegisterType<OCC::ActivityListModel>("com.nextcloud.desktopclient", 1, 0, "ActivityListModel");
     qmlRegisterType<OCC::FileActivityListModel>("com.nextcloud.desktopclient", 1, 0, "FileActivityListModel");
+    qmlRegisterType<Theme>("com.nextcloud.desktopclient", 1, 0, "Theme");
     qmlRegisterUncreatableType<OCC::UnifiedSearchResultsListModel>(
         "com.nextcloud.desktopclient", 1, 0, "UnifiedSearchResultsListModel", "UnifiedSearchResultsListModel");
     qRegisterMetaType<UnifiedSearchResultsListModel *>("UnifiedSearchResultsListModel*");

+ 2 - 1
src/gui/tray/ActivityActionButton.qml

@@ -2,6 +2,7 @@ import QtQuick 2.15
 import QtQuick.Controls 2.3
 import QtQuick.Layouts 1.15
 import Style 1.0
+import com.nextcloud.desktopclient 1.0
 
 Item {
     id: root
@@ -57,7 +58,7 @@ Item {
             imageSource: root.imageSource
             imageSourceHover: root.imageSourceHover
 
-            bgColor: Style.ncBlue
+            bgColor: UserModel.currentUser.headerColor
 
             onClicked: root.clicked()
         }

+ 2 - 1
src/gui/tray/ActivityItemActions.qml

@@ -3,6 +3,7 @@ import QtQuick 2.15
 import QtQuick.Controls 2.3
 import QtQuick.Layouts 1.2
 import Style 1.0
+import com.nextcloud.desktopclient 1.0
 
 RowLayout {
     id: root
@@ -42,7 +43,7 @@ RowLayout {
             imageSource: model.modelData.imageSource
             imageSourceHover: model.modelData.imageSourceHovered
 
-            textColor: imageSource !== "" ? Style.ncBlue : Style.unifiedSearchResulSublineColor
+            textColor: imageSource !== "" ? UserModel.currentUser.headerColor : Style.unifiedSearchResulSublineColor
             textColorHovered: imageSource !== "" ? Style.lightHover : Style.unifiedSearchResulTitleColor
 
             bold: primary

+ 2 - 2
src/gui/tray/ActivityItemContent.qml

@@ -121,12 +121,12 @@ RowLayout {
 
         visible: root.activityData.isShareable
 
-        imageSource: "image://svgimage-custom-color/share.svg" + "/" + Style.ncBlue
+        imageSource: "image://svgimage-custom-color/share.svg" + "/" + UserModel.currentUser.headerColor
         imageSourceHover: "image://svgimage-custom-color/share.svg" + "/" + Style.ncTextColor
 
         toolTipText: qsTr("Open share dialog")
 
-        bgColor: Style.ncBlue
+        bgColor: UserModel.currentUser.headerColor
 
         onClicked: root.shareButtonClicked()
     }

+ 3 - 1
src/gui/tray/UnifiedSearchInputContainer.qml

@@ -4,6 +4,8 @@ import QtQuick.Controls 2.3
 import QtGraphicalEffects 1.0
 import Style 1.0
 
+import com.nextcloud.desktopclient 1.0
+
 TextField {
     id: trayWindowUnifiedSearchTextField
 
@@ -28,7 +30,7 @@ TextField {
 
     background: Rectangle {
         radius: 5
-        border.color: parent.activeFocus ? Style.ncBlue : Style.menuBorder
+        border.color: parent.activeFocus ? UserModel.currentUser.accentColor : Style.menuBorder
         border.width: 1
     }
 

+ 2 - 1
src/gui/tray/UnifiedSearchResultSectionItem.qml

@@ -3,6 +3,7 @@ import QtQuick 2.15
 import QtQuick.Controls 2.15
 import QtQuick.Layouts 1.2
 import Style 1.0
+import com.nextcloud.desktopclient 1.0
 
 Label {
     required property string section
@@ -13,7 +14,7 @@ Label {
 
     text: section
     font.pixelSize: Style.topLinePixelSize
-    color: Style.ncBlue
+    color: UserModel.currentUser.accentColor
 
     Accessible.role: Accessible.Separator
     Accessible.name: qsTr("Search results section %1").arg(section)

+ 34 - 21
src/gui/tray/Window.qml

@@ -122,7 +122,7 @@ Window {
             anchors.right:  trayWindowBackground.right
             anchors.top:    trayWindowBackground.top
             height:         Style.trayWindowHeaderHeight
-            color:          Style.ncBlue
+            color:          UserModel.currentUser.headerColor
 
             RowLayout {
                 id: trayWindowHeaderLayout
@@ -363,7 +363,7 @@ Window {
                                 height: width
                                 anchors.bottom: currentAccountAvatar.bottom
                                 anchors.right: currentAccountAvatar.right
-                                color: Style.ncBlue
+                                color: UserModel.currentUser.headerColor
                                 radius: width*0.5
                             }
 
@@ -375,7 +375,7 @@ Window {
                                 height: width
                                 anchors.bottom: currentAccountAvatar.bottom
                                 anchors.right: currentAccountAvatar.right
-                                color: accountBtnMouseArea.containsMouse ? "white" : "transparent"
+                                color: currentAccountButton.hovered ? "white" : "transparent"
                                 opacity: 0.2
                                 radius: width*0.5
                             }
@@ -410,7 +410,7 @@ Window {
                                 width: Style.currentAccountLabelWidth
                                 text: UserModel.currentUser.name
                                 elide: Text.ElideRight
-                                color: Style.ncTextColor
+                                color: UserModel.currentUser.headerTextColor
                                 font.pixelSize: Style.topLinePixelSize
                                 font.bold: true
                             }
@@ -438,7 +438,7 @@ Window {
                                           ? UserModel.currentUser.statusMessage
                                           : UserModel.currentUser.server
                                     elide: Text.ElideRight
-                                    color: Style.ncTextColor
+                                    color: UserModel.currentUser.headerTextColor
                                     font.pixelSize: Style.subLinePixelSize
                                 }
                             }
@@ -446,7 +446,7 @@ Window {
 
                         ColorOverlay {
                             cached: true
-                            color: Style.ncTextColor
+                            color: UserModel.currentUser.headerTextColor
                             width: source.width
                             height: source.height
                             source: Image {
@@ -475,23 +475,16 @@ Window {
                     Layout.preferredHeight: Style.trayWindowHeaderHeight
                     Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
 
+                    Accessible.role: Accessible.Button
+                    Accessible.name: qsTr("Open local folder of current account")
+
                     HeaderButton {
                         id: openLocalFolderButton
                         visible: UserModel.currentUser.hasLocalFolder
                         icon.source: "qrc:///client/theme/white/folder.svg"
+                        icon.color: UserModel.currentUser.headerTextColor
                         onClicked: UserModel.openCurrentAccountLocalFolder()
 
-                        Rectangle {
-                            id: folderStateIndicatorBackground
-                            width: Style.folderStateIndicatorSize
-                            height: width
-                            anchors.top: openLocalFolderButton.verticalCenter
-                            anchors.left: openLocalFolderButton.horizontalCenter
-                            color: Style.ncBlue
-                            radius: width*0.5
-                            z: 1
-                        }
-
                         Image {
                             id: folderStateIndicator
                             visible: UserModel.currentUser.hasLocalFolder
@@ -507,12 +500,30 @@ Window {
 
                             Accessible.role: Accessible.Indicator
                             Accessible.name: UserModel.currentUser.isConnected ? qsTr("Connected") : qsTr("Disconnected")
-                            z: 2
+                            z: 1
+
+                            Rectangle {
+                                id: folderStateIndicatorBackground
+                                width: Style.folderStateIndicatorSize + 2
+                                height: width
+                                anchors.centerIn: parent
+                                color: UserModel.currentUser.headerColor
+                                radius: width*0.5
+                                z: -2
+                            }
+
+                            Rectangle {
+                                id: folderStateIndicatorBackgroundMouseHover
+                                width: Style.folderStateIndicatorSize + 2
+                                height: width
+                                anchors.centerIn: parent
+                                color: openLocalFolderButton.hovered ? "white" : "transparent"
+                                opacity: 0.2
+                                radius: width*0.5
+                                z: -1
+                            }
                         }
                     }
-
-                    Accessible.role: Accessible.Button
-                    Accessible.name: qsTr("Open local folder of current account")
                 }
 
                 HeaderButton {
@@ -520,6 +531,7 @@ Window {
 
                     visible: UserModel.currentUser.serverHasTalk
                     icon.source: "qrc:///client/theme/white/talk-app.svg"
+                    icon.color: UserModel.currentUser.headerTextColor
                     onClicked: UserModel.openCurrentAccountTalk()
 
                     Accessible.role: Accessible.Button
@@ -530,6 +542,7 @@ Window {
                 HeaderButton {
                     id: trayWindowAppsButton
                     icon.source: "qrc:///client/theme/white/more-apps.svg"
+                    icon.color: UserModel.currentUser.headerTextColor
 
                     onClicked: {
                         if(appsMenu.count <= 0) {

+ 19 - 0
src/gui/tray/usermodel.cpp

@@ -73,6 +73,10 @@ User::User(AccountStatePtr &account, const bool &isCurrent, QObject *parent)
     connect(_account->account().data(), &Account::userStatusChanged, this, &User::statusChanged);
     connect(_account.data(), &AccountState::desktopNotificationsAllowedChanged, this, &User::desktopNotificationsAllowedChanged);
 
+    connect(_account->account().data(), &Account::capabilitiesChanged, this, &User::headerColorChanged);
+    connect(_account->account().data(), &Account::capabilitiesChanged, this, &User::headerTextColorChanged);
+    connect(_account->account().data(), &Account::capabilitiesChanged, this, &User::accentColorChanged);
+
     connect(_activityModel, &ActivityListModel::sendNotificationRequest, this, &User::slotSendNotificationRequest);
 }
 
@@ -700,6 +704,21 @@ bool User::hasActivities() const
     return _account->account()->capabilities().hasActivities();
 }
 
+QColor User::headerColor() const
+{
+    return _account->account()->headerColor();
+}
+
+QColor User::headerTextColor() const
+{
+    return _account->account()->headerTextColor();
+}
+
+QColor User::accentColor() const
+{
+    return _account->account()->accentColor();
+}
+
 AccountAppList User::appList() const
 {
     return _account->appList();

+ 9 - 0
src/gui/tray/usermodel.h

@@ -25,6 +25,9 @@ class User : public QObject
     Q_OBJECT
     Q_PROPERTY(QString name READ name NOTIFY nameChanged)
     Q_PROPERTY(QString server READ server CONSTANT)
+    Q_PROPERTY(QColor headerColor READ headerColor NOTIFY headerColorChanged)
+    Q_PROPERTY(QColor headerTextColor READ headerTextColor NOTIFY headerTextColorChanged)
+    Q_PROPERTY(QColor accentColor READ accentColor NOTIFY accentColorChanged)
     Q_PROPERTY(bool serverHasUserStatus READ serverHasUserStatus CONSTANT)
     Q_PROPERTY(QUrl statusIcon READ statusIcon NOTIFY statusChanged)
     Q_PROPERTY(QString statusEmoji READ statusEmoji NOTIFY statusChanged)
@@ -55,6 +58,9 @@ public:
     bool serverHasUserStatus() const;
     AccountApp *talkApp() const;
     bool hasActivities() const;
+    QColor accentColor() const;
+    QColor headerColor() const;
+    QColor headerTextColor() const;
     AccountAppList appList() const;
     QImage avatar() const;
     void login() const;
@@ -77,6 +83,9 @@ signals:
     void accountStateChanged();
     void statusChanged();
     void desktopNotificationsAllowedChanged();
+    void headerColorChanged();
+    void headerTextColorChanged();
+    void accentColorChanged();
 
 public slots:
     void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item);

+ 31 - 0
src/libsync/account.cpp

@@ -160,6 +160,35 @@ void Account::setDavDisplayName(const QString &newDisplayName)
     emit accountChangedDisplayName();
 }
 
+QColor Account::headerColor() const
+{
+    const auto serverColor = capabilities().serverColor();
+    return serverColor.isValid() ? serverColor : Theme::defaultColor();
+}
+
+QColor Account::headerTextColor() const
+{
+    const auto headerTextColor = capabilities().serverTextColor();
+    return headerTextColor.isValid() ? headerTextColor : QColor(255,255,255);
+}
+
+QColor Account::accentColor() const
+{
+    // This will need adjusting when dark theme is a thing
+    auto serverColor = capabilities().serverColor();
+
+    if(!serverColor.isValid()) {
+        serverColor = Theme::defaultColor();
+    }
+
+    const auto effectMultiplier = 8;
+    auto darknessAdjustment = static_cast<int>((1 - Theme::getColorDarkness(serverColor)) * effectMultiplier);
+    darknessAdjustment *= darknessAdjustment; // Square the value to pronounce the darkness more in lighter colours
+    const auto baseAdjustment = 125;
+    const auto adjusted = Theme::isDarkColor(serverColor) ? serverColor : serverColor.darker(baseAdjustment + darknessAdjustment);
+    return adjusted;
+}
+
 QString Account::id() const
 {
     return _id;
@@ -587,6 +616,8 @@ void Account::setCapabilities(const QVariantMap &caps)
 {
     _capabilities = Capabilities(caps);
 
+    emit capabilitiesChanged();
+
     setupUserStatusConnector();
     trySetupPushNotifications();
 }

+ 6 - 0
src/libsync/account.h

@@ -109,6 +109,10 @@ public:
     /// The name of the account as shown in the toolbar
     QString displayName() const;
 
+    QColor accentColor() const;
+    QColor headerColor() const;
+    QColor headerTextColor() const;
+
     /// The internal id of the account.
     QString id() const;
 
@@ -305,6 +309,8 @@ signals:
 
     void userStatusChanged();
 
+    void capabilitiesChanged();
+
 protected Q_SLOTS:
     void slotCredentialsFetched();
     void slotCredentialsAsked();

+ 23 - 0
src/libsync/capabilities.cpp

@@ -239,6 +239,29 @@ bool Capabilities::userStatusSupportsEmoji() const
     return userStatusMap.value("supports_emoji", false).toBool();
 }
 
+QColor Capabilities::serverColor() const
+{
+    const auto themingMap = serverThemingMap();
+    return themingMap.contains("color") ? QColor(themingMap["color"].toString()) : QColor();
+}
+
+QColor Capabilities::serverTextColor() const
+{
+    const auto themingMap = serverThemingMap();
+    return themingMap.contains("color-text") ? QColor(themingMap["color-text"].toString()) : QColor();
+}
+
+QMap<QString, QVariant> Capabilities::serverThemingMap() const
+{
+    if (!_capabilities.contains("theming")) {
+        return {};
+    }
+
+    return _capabilities["theming"].toMap();
+}
+
+
+
 PushNotificationTypes Capabilities::availablePushNotifications() const
 {
     if (!_capabilities.contains("notify_push")) {

+ 5 - 0
src/libsync/capabilities.h

@@ -21,6 +21,7 @@
 #include <QVariantMap>
 #include <QStringList>
 #include <QMimeDatabase>
+#include <QColor>
 
 namespace OCC {
 
@@ -66,6 +67,8 @@ public:
     bool bulkUpload() const;
     bool userStatus() const;
     bool userStatusSupportsEmoji() const;
+    QColor serverColor() const;
+    QColor serverTextColor() const;
 
     /// Returns which kind of push notfications are available
     PushNotificationTypes availablePushNotifications() const;
@@ -167,6 +170,8 @@ public:
     DirectEditor* getDirectEditorForOptionalMimetype(const QMimeType &mimeType);
 
 private:
+    QMap<QString, QVariant> serverThemingMap() const;
+
     QVariantMap _capabilities;
 
     QList<DirectEditor*> _directEditors;

+ 13 - 3
src/libsync/theme.cpp

@@ -776,11 +776,16 @@ QString Theme::versionSwitchOutput() const
     return helpText;
 }
 
-bool Theme::isDarkColor(const QColor &color)
+double Theme::getColorDarkness(const QColor &color)
 {
     // account for different sensitivity of the human eye to certain colors
-    double treshold = 1.0 - (0.299 * color.red() + 0.587 * color.green() + 0.114 * color.blue()) / 255.0;
-    return treshold > 0.5;
+    const double threshold = 1.0 - (0.299 * color.red() + 0.587 * color.green() + 0.114 * color.blue()) / 255.0;
+    return threshold;
+}
+
+bool Theme::isDarkColor(const QColor &color)
+{
+    return getColorDarkness(color) > 0.5;
 }
 
 QColor Theme::getBackgroundAwareLinkColor(const QColor &backgroundColor)
@@ -874,6 +879,11 @@ bool Theme::enforceVirtualFilesSyncFolder() const
     return ENFORCE_VIRTUAL_FILES_SYNC_FOLDER && vfsMode != OCC::Vfs::Off;
 }
 
+QColor Theme::defaultColor()
+{
+    return QColor{NEXTCLOUD_BACKGROUND_COLOR};
+}
+
 QColor Theme::errorBoxTextColor() const
 {
     return QColor{"white"};

+ 7 - 2
src/libsync/theme.h

@@ -62,6 +62,7 @@ class OWNCLOUDSYNC_EXPORT Theme : public QObject
 #endif
     Q_PROPERTY(QString updateCheckUrl READ updateCheckUrl CONSTANT)
 
+    Q_PROPERTY(QColor defaultColor READ defaultColor CONSTANT)
     Q_PROPERTY(QColor errorBoxTextColor READ errorBoxTextColor CONSTANT)
     Q_PROPERTY(QColor errorBoxBackgroundColor READ errorBoxBackgroundColor CONSTANT)
     Q_PROPERTY(QColor errorBoxBorderColor READ errorBoxBorderColor CONSTANT)
@@ -472,7 +473,9 @@ public:
     * (actually 2019/09/13 only systray theming).
     */
 	virtual QIcon uiThemeIcon(const QString &iconName, bool uiHasDarkBg) const;
-    
+
+    Q_INVOKABLE static double getColorDarkness(const QColor &color);
+
     /**
      * @brief Perform a calculation to check if a colour is dark or light and accounts for different sensitivity of the human eye.
      *
@@ -480,7 +483,7 @@ public:
      *
      * 2019/12/08: Moved here from SettingsDialog.
      */
-    static bool isDarkColor(const QColor &color);
+    Q_INVOKABLE static bool isDarkColor(const QColor &color);
     
     /**
      * @brief Return the colour to be used for HTML links (e.g. used in QLabel), based on the current app palette or given colour (Dark-/Light-Mode switching).
@@ -574,6 +577,8 @@ public:
 
     virtual bool enforceVirtualFilesSyncFolder() const;
 
+    static QColor defaultColor();
+
     /** @return color for the ErrorBox text. */
     virtual QColor errorBoxTextColor() const;