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

color

Signed-off-by: Claudio Cambra <claudio.cambra@gmail.com>
This commit is contained in:
Claudio Cambra 2022-01-28 17:03:39 +01:00
parent 454ae37e8d
commit f585b8bd48
15 changed files with 159 additions and 32 deletions

View file

@ -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*");

View file

@ -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()
}

View file

@ -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

View file

@ -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()
}

View file

@ -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
}

View file

@ -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)

View file

@ -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) {

View file

@ -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();

View file

@ -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);

View file

@ -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();
}

View file

@ -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();

View file

@ -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")) {

View file

@ -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;

View file

@ -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"};

View file

@ -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;