mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-22 21:15:55 +03:00
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:
parent
454ae37e8d
commit
f585b8bd48
15 changed files with 159 additions and 32 deletions
|
@ -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,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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: qsTr("Open local folder of current account")
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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")) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"};
|
||||
|
|
|
@ -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)
|
||||
|
@ -473,6 +474,8 @@ public:
|
|||
*/
|
||||
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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue