diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt --- a/src/widgets/CMakeLists.txt +++ b/src/widgets/CMakeLists.txt @@ -90,6 +90,7 @@ set(Ruqola_misc_widget_SRCS misc/accountmenu.cpp + misc/accountsoverviewwidget.cpp misc/emoticonmenuwidget.cpp misc/emoticonselectorwidget.cpp ) diff --git a/src/widgets/misc/accountsoverviewwidget.h b/src/widgets/misc/accountsoverviewwidget.h new file mode 100644 --- /dev/null +++ b/src/widgets/misc/accountsoverviewwidget.h @@ -0,0 +1,40 @@ +/* + * Copyright 2020 Olivier de Gaalon + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#pragma once + +#include +#include + +class AccountButton; + +class AccountsOverviewWidget : public QWidget +{ + Q_OBJECT +public: + AccountsOverviewWidget(QWidget *parent = nullptr); + +private: + void updateButtons(); + +private: + QVector mAccounts; +}; diff --git a/src/widgets/misc/accountsoverviewwidget.cpp b/src/widgets/misc/accountsoverviewwidget.cpp new file mode 100644 --- /dev/null +++ b/src/widgets/misc/accountsoverviewwidget.cpp @@ -0,0 +1,216 @@ +/* + * Copyright 2020 Olivier de Gaalon + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "accountsoverviewwidget.h" + +#include "accountmanager.h" +#include "model/rocketchataccountmodel.h" +#include "rocketchataccount.h" +#include "ruqola.h" + +#include + +#include +#include +#include +#include +#include +#include + +constexpr const double PAD = 0.2; + +class AccountButton : public QAbstractButton +{ + struct UnreadAlert + { + int unread; + bool alert; + }; + +public: + AccountButton(QWidget *parent = nullptr) + : QAbstractButton(parent) + , mAccount(nullptr) + { + setMouseTracking(true); + setFocusPolicy(Qt::NoFocus); + setAttribute(Qt::WA_Hover); + } + + void setAccount(RocketChatAccount *acct) + { + if (mAccount) + { + mAccount->disconnect(this); + mAccount->roomModel()->disconnect(this); + this->disconnect(acct); + } + + mAccount = acct; + + if (mAccount) + { + auto updateFont = [this]{ + QFont f = font(); + f.setBold(currentUnreadAlert().alert); + setFont(f); + }; + connect(acct, &RocketChatAccount::accountNameChanged, this, &AccountButton::updateGeometry); + connect(acct, &RocketChatAccount::loginStatusChanged, this, &AccountButton::updateGeometry); + connect(acct, &RocketChatAccount::loginStatusChanged, this, &AccountButton::updateTooltip); + connect(acct->roomModel(), &RoomModel::needToUpdateNotification, this, updateFont); + connect(this, &AccountButton::clicked, acct, [acct]{ + Ruqola::self()->accountManager()->setCurrentAccount(acct->accountName()); + }); + + updateFont(); + } + update(); + } + + QSize sizeHint() const override + { + const double height = fontMetrics().height(); + const double padding = height * PAD; + const QSize text = fontMetrics().boundingRect(currentText()).size(); + return QSize(text.width() + padding * 2, height + padding * 2); + } + +protected: + void enterEvent(QEvent *event) override + { + if (isEnabled()) + update(); + QAbstractButton::enterEvent(event); + } + + void leaveEvent(QEvent *event) override + { + if (isEnabled()) + update(); + QAbstractButton::leaveEvent(event); + } + + void paintEvent(QPaintEvent *) override + { + if (!mAccount) + return; + + QPainter p(this); + + { + QStyleOption opt; + opt.init(this); + if (isDown()) + opt.state |= QStyle::State_Sunken; + style()->drawPrimitive(QStyle::PE_PanelButtonTool, &opt, &p, this); + } + + p.setPen(palette().color(QPalette::WindowText)); + p.setFont(font()); + p.drawText(rect(), Qt::AlignCenter, currentText()); + } + +private: + QString currentText() const + { + QString text = mAccount->accountName(); + if (text.isEmpty()) + text = i18n("(Unnamed)"); + + if (mAccount->loginStatus() != DDPClient::LoggedIn) + text += QStringLiteral(": %1").arg(currentLoginStatusText()); + else if (int unread = currentUnreadAlert().unread) + text += QStringLiteral(" (%1)").arg(unread); + + return text; + } + + UnreadAlert currentUnreadAlert() const + { + UnreadAlert ua = {0, false}; + mAccount->roomModel()->getUnreadAlertFromAccount(ua.alert, ua.unread); + return ua; + } + + QString currentLoginStatusText() const + { + if (mAccount) + { + switch (mAccount->loginStatus()) + { + case DDPClient::NotConnected: + return i18n("Not connected"); + case DDPClient::LoginCodeRequired: + return i18n("Login code required"); + case DDPClient::LoginFailed: + return i18n("Login failed"); + case DDPClient::LoggingIn: + return i18n("Logging in"); + case DDPClient::LoggedIn: + return i18n("Logged in"); + case DDPClient::LoggedOut: + return i18n("Logged out"); + case DDPClient::FailedToLoginPluginProblem: + return i18n("Failed to login due to plugin problem"); + } + } + return i18n("Unknown state"); + } + + void updateTooltip() + { + setToolTip(currentLoginStatusText()); + } + + QPointer mAccount; +}; + +AccountsOverviewWidget::AccountsOverviewWidget(QWidget *parent) + : QWidget(parent) +{ + setLayout(new QHBoxLayout); + const auto model = Ruqola::self()->accountManager()->rocketChatAccountModel(); + connect(model, &RocketChatAccountModel::rowsInserted, this, &AccountsOverviewWidget::updateButtons); + connect(model, &RocketChatAccountModel::rowsRemoved, this, &AccountsOverviewWidget::updateButtons); + connect(model, &RocketChatAccountModel::modelReset, this, &AccountsOverviewWidget::updateButtons); + updateButtons(); +} + +void AccountsOverviewWidget::updateButtons() +{ + const auto model = Ruqola::self()->accountManager()->rocketChatAccountModel(); + const auto count = model->rowCount(); + for (int i = 0; i < count; ++i) + { + if (i >= mAccounts.size()) + { + mAccounts.append(new AccountButton(this)); + layout()->addWidget(mAccounts.last()); + } + mAccounts[i]->setAccount(model->account(i)); + } + for (int i = count; i < mAccounts.size(); ++i) + { + mAccounts[i]->deleteLater(); + } + mAccounts.resize(count); +} diff --git a/src/widgets/ruqolamainwindow.cpp b/src/widgets/ruqolamainwindow.cpp --- a/src/widgets/ruqolamainwindow.cpp +++ b/src/widgets/ruqolamainwindow.cpp @@ -26,6 +26,7 @@ #include "ruqolamainwindow.h" #include "ruqolacentralwidget.h" #include "misc/accountmenu.h" +#include "misc/accountsoverviewwidget.h" #include "dialogs/serverinfodialog.h" #include "dialogs/searchchanneldialog.h" #include "dialogs/createnewchanneldialog.h" @@ -86,6 +87,7 @@ mStatusBarTypingMessage->setTextFormat(Qt::RichText); mStatusBarTypingMessage->setObjectName(QStringLiteral("mStatusBarTypingMessage")); statusBar()->addPermanentWidget(mStatusBarTypingMessage); + statusBar()->addPermanentWidget(new AccountsOverviewWidget(this)); } void RuqolaMainWindow::slotAccountChanged()