Use dynamic path for account online/offline state icon. Refresh GUI on connection state change.

Signed-off-by: Alex Zolotov <alex.zolotov.nextcloud.com>
Signed-off-by: allexzander <blackslayer4@gmail.com>
This commit is contained in:
allexzander 2020-11-23 07:45:26 +02:00
parent 10cb0a71eb
commit 665a8c4217
7 changed files with 106 additions and 9 deletions

View file

@ -81,7 +81,9 @@ MenuItem {
} }
Image { Image {
id: accountStateIndicator id: accountStateIndicator
source: isConnected ? "qrc:///client/theme/colored/state-ok.svg" : "qrc:///client/theme/colored/state-offline.svg" source: model.isConnected
? Style.stateOnlineImageSource
: Style.stateOfflineImageSource
cache: false cache: false
x: accountStateIndicatorBackground.x + 1 x: accountStateIndicatorBackground.x + 1
y: accountStateIndicatorBackground.y + 1 y: accountStateIndicatorBackground.y + 1
@ -89,7 +91,7 @@ MenuItem {
sourceSize.height: Style.accountAvatarStateIndicatorSize sourceSize.height: Style.accountAvatarStateIndicatorSize
Accessible.role: Accessible.Indicator Accessible.role: Accessible.Indicator
Accessible.name: isConnected ? qsTr("Account connected") : qsTr("Account not connected") Accessible.name: model.isConnected ? qsTr("Account connected") : qsTr("Account not connected")
} }
} }
@ -163,11 +165,11 @@ MenuItem {
} }
MenuItem { MenuItem {
text: isConnected ? qsTr("Log out") : qsTr("Log in") text: model.isConnected ? qsTr("Log out") : qsTr("Log in")
font.pixelSize: Style.topLinePixelSize font.pixelSize: Style.topLinePixelSize
hoverEnabled: true hoverEnabled: true
onClicked: { onClicked: {
isConnected ? UserModel.logout(index) : UserModel.login(index) model.isConnected ? UserModel.logout(index) : UserModel.login(index)
accountMenu.close() accountMenu.close()
} }
@ -182,10 +184,10 @@ MenuItem {
} }
Accessible.role: Accessible.Button Accessible.role: Accessible.Button
Accessible.name: isConnected ? qsTr("Log out") : qsTr("Log in") Accessible.name: model.isConnected ? qsTr("Log out") : qsTr("Log in")
onPressed: { onPressed: {
if (isConnected) { if (model.isConnected) {
UserModel.logout(index) UserModel.logout(index)
} else { } else {
UserModel.login(index) UserModel.login(index)
@ -221,4 +223,13 @@ MenuItem {
} }
} }
} }
Connections {
target: UserModel
onRefreshCurrentUserGui: {
accountStateIndicator.source = model.isConnected
? Style.stateOnlineImageSource
: Style.stateOfflineImageSource
}
}
} // MenuItem userLine } // MenuItem userLine

View file

@ -41,6 +41,7 @@ User::User(AccountStatePtr &account, const bool &isCurrent, QObject *parent)
connect(_account.data(), &AccountState::stateChanged, connect(_account.data(), &AccountState::stateChanged,
[=]() { if (isConnected()) {slotRefresh();} }); [=]() { if (isConnected()) {slotRefresh();} });
connect(_account.data(), &AccountState::stateChanged, this, &User::accountStateChanged);
connect(_account.data(), &AccountState::hasFetchedNavigationApps, connect(_account.data(), &AccountState::hasFetchedNavigationApps,
this, &User::slotRebuildNavigationAppList); this, &User::slotRebuildNavigationAppList);
connect(_account->account().data(), &Account::accountChangedDisplayName, this, &User::nameChanged); connect(_account->account().data(), &Account::accountChangedDisplayName, this, &User::nameChanged);
@ -552,6 +553,7 @@ void UserModel::buildUserList()
} }
if (_init) { if (_init) {
_users.first()->setCurrentUser(true); _users.first()->setCurrentUser(true);
connect(_users.first(), &User::accountStateChanged, this, &UserModel::refreshCurrentUserGui);
_init = false; _init = false;
} }
} }
@ -613,6 +615,7 @@ void UserModel::addUser(AccountStatePtr &user, const bool &isCurrent)
_users << u; _users << u;
if (isCurrent) { if (isCurrent) {
_currentUserId = _users.indexOf(_users.last()); _currentUserId = _users.indexOf(_users.last());
connect(u, &User::accountStateChanged, this, &UserModel::refreshCurrentUserGui);
} }
endInsertRows(); endInsertRows();
@ -665,8 +668,10 @@ Q_INVOKABLE void UserModel::switchCurrentUser(const int &id)
if (_users.isEmpty()) if (_users.isEmpty())
return; return;
disconnect(_users[_currentUserId], &User::accountStateChanged, this, &UserModel::refreshCurrentUserGui);
_users[_currentUserId]->setCurrentUser(false); _users[_currentUserId]->setCurrentUser(false);
_users[id]->setCurrentUser(true); _users[id]->setCurrentUser(true);
connect(_users[id], &User::accountStateChanged, this, &UserModel::refreshCurrentUserGui);
_currentUserId = id; _currentUserId = id;
emit refreshCurrentUserGui(); emit refreshCurrentUserGui();
emit newUserSelected(); emit newUserSelected();
@ -710,6 +715,10 @@ Q_INVOKABLE void UserModel::removeAccount(const int &id)
return; return;
} }
if (_users[id]->isCurrentUser()) {
disconnect(_users[id], &User::accountStateChanged, this, &UserModel::refreshCurrentUserGui);
}
if (_users[id]->isCurrentUser() && _users.count() > 1) { if (_users[id]->isCurrentUser() && _users.count() > 1) {
id == 0 ? switchCurrentUser(1) : switchCurrentUser(0); id == 0 ? switchCurrentUser(1) : switchCurrentUser(0);
} }

View file

@ -52,6 +52,7 @@ signals:
void hasLocalFolderChanged(); void hasLocalFolderChanged();
void serverHasTalkChanged(); void serverHasTalkChanged();
void avatarChanged(); void avatarChanged();
void accountStateChanged(int state);
public slots: public slots:
void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item); void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item);

View file

@ -36,7 +36,9 @@ Window {
onVisibleChanged: { onVisibleChanged: {
currentAccountStateIndicator.source = "" currentAccountStateIndicator.source = ""
currentAccountStateIndicator.source = UserModel.isUserConnected(UserModel.currentUserId) ? "qrc:///client/theme/colored/state-ok.svg" : "qrc:///client/theme/colored/state-offline.svg" currentAccountStateIndicator.source = UserModel.isUserConnected(UserModel.currentUserId)
? Style.stateOnlineImageSource
: Style.stateOfflineImageSource
// HACK: reload account Instantiator immediately by restting it - could be done better I guess // HACK: reload account Instantiator immediately by restting it - could be done better I guess
// see also id:accountMenu below // see also id:accountMenu below
@ -48,7 +50,9 @@ Window {
target: UserModel target: UserModel
onRefreshCurrentUserGui: { onRefreshCurrentUserGui: {
currentAccountStateIndicator.source = "" currentAccountStateIndicator.source = ""
currentAccountStateIndicator.source = UserModel.isUserConnected(UserModel.currentUserId) ? "qrc:///client/theme/colored/state-ok.svg" : "qrc:///client/theme/colored/state-offline.svg" currentAccountStateIndicator.source = UserModel.isUserConnected(UserModel.currentUserId)
? Style.stateOnlineImageSource
: Style.stateOfflineImageSource
} }
onNewUserSelected: { onNewUserSelected: {
accountMenu.close(); accountMenu.close();
@ -357,7 +361,9 @@ Window {
Image { Image {
id: currentAccountStateIndicator id: currentAccountStateIndicator
source: UserModel.isUserConnected(UserModel.currentUserId) ? "qrc:///client/theme/colored/state-ok.svg" : "qrc:///client/theme/colored/state-offline.svg" source: UserModel.isUserConnected(UserModel.currentUserId)
? Style.stateOnlineImageSource
: Style.stateOfflineImageSource
cache: false cache: false
x: currentAccountStateIndicatorBackground.x + 1 x: currentAccountStateIndicatorBackground.x + 1
y: currentAccountStateIndicatorBackground.y + 1 y: currentAccountStateIndicatorBackground.y + 1

View file

@ -36,6 +36,22 @@
#undef Mirall #undef Mirall
#endif #endif
namespace {
QUrl imagePathToUrl(const QString &imagePath)
{
if (imagePath.startsWith(':')) {
auto url = QUrl();
url.setScheme(QStringLiteral("qrc"));
url.setPath(imagePath.mid(1));
return url;
} else {
return QUrl::fromLocalFile(imagePath);
}
}
}
namespace OCC { namespace OCC {
Theme *Theme::_instance = nullptr; Theme *Theme::_instance = nullptr;
@ -106,6 +122,16 @@ QString Theme::appName() const
return APPLICATION_SHORTNAME; return APPLICATION_SHORTNAME;
} }
QUrl Theme::stateOnlineImageSource() const
{
return imagePathToUrl(themeImagePath("state-ok"));
}
QUrl Theme::stateOfflineImageSource() const
{
return imagePathToUrl(themeImagePath("state-offline", 16));
}
QString Theme::version() const QString Theme::version() const
{ {
return MIRALL_VERSION_STRING; return MIRALL_VERSION_STRING;
@ -188,6 +214,25 @@ QIcon Theme::themeIcon(const QString &name, bool sysTray) const
return cached; return cached;
} }
QString Theme::themeImagePath(const QString &name, int size, bool sysTray) const
{
const auto flavor = (!isBranded() && sysTray) ? systrayIconFlavor(_mono) : QLatin1String("colored");
// branded client may have several sizes of the same icon
const QString filePath = (isBranded() && size > 0)
? QString::fromLatin1(":/client/theme/%1/%2-%3").arg(flavor).arg(name).arg(size)
: QString::fromLatin1(":/client/theme/%1/%2").arg(flavor).arg(name);
const QString brandedImagePath = filePath + ".png";
// only return branded .png image path if it exists, or fall-back to non-branded .svg otherwise
if (isBranded() && QFile::exists(brandedImagePath)) {
return brandedImagePath;
}
return filePath + ".svg";
}
QIcon Theme::uiThemeIcon(const QString &iconName, bool uiHasDarkBg) const QIcon Theme::uiThemeIcon(const QString &iconName, bool uiHasDarkBg) const
{ {
QString themeResBasePath = ":/client/theme/"; QString themeResBasePath = ":/client/theme/";

View file

@ -40,6 +40,8 @@ class OWNCLOUDSYNC_EXPORT Theme : public QObject
Q_PROPERTY(bool branded READ isBranded CONSTANT) Q_PROPERTY(bool branded READ isBranded CONSTANT)
Q_PROPERTY(QString appNameGUI READ appNameGUI CONSTANT) Q_PROPERTY(QString appNameGUI READ appNameGUI CONSTANT)
Q_PROPERTY(QString appName READ appName CONSTANT) Q_PROPERTY(QString appName READ appName CONSTANT)
Q_PROPERTY(QUrl stateOnlineImageSource READ stateOnlineImageSource CONSTANT)
Q_PROPERTY(QUrl stateOfflineImageSource READ stateOfflineImageSource CONSTANT)
#ifndef TOKEN_AUTH_ONLY #ifndef TOKEN_AUTH_ONLY
Q_PROPERTY(QIcon folderDisabledIcon READ folderDisabledIcon CONSTANT) Q_PROPERTY(QIcon folderDisabledIcon READ folderDisabledIcon CONSTANT)
Q_PROPERTY(QIcon folderOfflineIcon READ folderOfflineIcon CONSTANT) Q_PROPERTY(QIcon folderOfflineIcon READ folderOfflineIcon CONSTANT)
@ -109,6 +111,18 @@ public:
*/ */
virtual QString appName() const; virtual QString appName() const;
/**
* @brief Returns full path to an online state icon
* @return QUrl full path to an icon
*/
QUrl stateOnlineImageSource() const;
/**
* @brief Returns full path to an offline state icon
* @return QUrl full path to an icon
*/
QUrl stateOfflineImageSource() const;
/** /**
* @brief configFileName * @brief configFileName
* @return the name of the config file. * @return the name of the config file.
@ -491,6 +505,14 @@ 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;
#endif #endif
/**
* @brief Generates image path in the resources
* @param name Name of the image file
* @param size Size in the power of two (16, 32, 64, etc.)
* @param sysTray Whether the image requested is for Systray or not
* @return QString image path in the resources
**/
QString themeImagePath(const QString &name, int size = -1, bool sysTray = false) const;
Theme(); Theme();
signals: signals:

View file

@ -28,6 +28,9 @@ QtObject {
property int currentAccountButtonRadius: 2 property int currentAccountButtonRadius: 2
property int currentAccountLabelWidth: 128 property int currentAccountLabelWidth: 128
property url stateOnlineImageSource: Theme.stateOnlineImageSource
property url stateOfflineImageSource: Theme.stateOfflineImageSource
property int accountAvatarSize: (trayWindowHeaderHeight - 16) property int accountAvatarSize: (trayWindowHeaderHeight - 16)
property int accountAvatarStateIndicatorSize: 16 property int accountAvatarStateIndicatorSize: 16
property int accountLabelWidth: 128 property int accountLabelWidth: 128