summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Swift/Controllers/MainController.h1
-rw-r--r--Swift/QtUI/QtSingleWindow.cpp29
-rw-r--r--Swift/QtUI/QtSingleWindow.h9
-rw-r--r--Swift/QtUI/SConscript3
-rw-r--r--Swift/QtUI/ServerList/QtServerListView.cpp31
-rw-r--r--Swift/QtUI/ServerList/QtServerListView.h25
-rw-r--r--Swift/QtUI/ServerList/ServerListDelegate.cpp117
-rw-r--r--Swift/QtUI/ServerList/ServerListDelegate.h31
-rw-r--r--Swift/QtUI/ServerList/ServerListModel.cpp68
-rw-r--r--Swift/QtUI/ServerList/ServerListModel.h87
10 files changed, 386 insertions, 15 deletions
diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h
index 7a06a0b..b345e0e 100644
--- a/Swift/Controllers/MainController.h
+++ b/Swift/Controllers/MainController.h
@@ -101,7 +101,6 @@ namespace Swift {
bool useDelayForLatency);
~MainController();
-
private:
void resetClient();
void handleConnected();
diff --git a/Swift/QtUI/QtSingleWindow.cpp b/Swift/QtUI/QtSingleWindow.cpp
index 6881c4f..af7e552 100644
--- a/Swift/QtUI/QtSingleWindow.cpp
+++ b/Swift/QtUI/QtSingleWindow.cpp
@@ -14,6 +14,8 @@
#include <Swift/QtUI/QtChatTabs.h>
#include <Swift/QtUI/QtLoginWindow.h>
#include <Swift/QtUI/QtSettingsProvider.h>
+#include <Swift/QtUI/ServerList/QtServerListView.h>
+#include <Swift/QtUI/ServerList/ServerListModel.h>
namespace Swift {
@@ -30,10 +32,14 @@ QtSingleWindow::QtSingleWindow(QtSettingsProvider* settings) : QSplitter() {
setChildrenCollapsible(false);
auto left = new QWidget(this);
- list_ = new QListWidget(left);
+ serverList_ = new QtServerListView();
+ serverListModel_ = new ServerListModel();
+ serverList_->setModel(serverListModel_);
+ serverListModel_->setModelData(&accountData_);
+ accountData_.onDataChanged.connect(boost::bind(&ServerListModel::handleDataChanged, serverListModel_));
auto addButton = new QPushButton("+", left);
QVBoxLayout* leftLayout = new QVBoxLayout();
- leftLayout->addWidget(list_);
+ leftLayout->addWidget(serverList_);
leftLayout->addWidget(addButton);
left->setLayout(leftLayout);
QSplitter::addWidget(left);
@@ -45,8 +51,8 @@ QtSingleWindow::QtSingleWindow(QtSettingsProvider* settings) : QSplitter() {
setStretchFactor(0, 0);
setStretchFactor(1, 0);
setStretchFactor(2, 1);
- connect(list_, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(handleListItemClicked(QListWidgetItem*)));
- connect(addButton, SIGNAL(clicked()), this, SIGNAL(wantsToAddAccount()));
+ connect(serverList_, &QtServerListView::clicked, this, &QtSingleWindow::handleListItemClicked);
+ connect(addButton, &QPushButton::clicked, this, &QtSingleWindow::wantsToAddAccount);
#ifdef SWIFTEN_PLATFORM_MACOSX
setHandleWidth(0);
#endif
@@ -98,24 +104,23 @@ void QtSingleWindow::moveEvent(QMoveEvent*) {
}
void QtSingleWindow::addAccount(QtLoginWindow* loginWindow, QtChatTabs* tabs) {
- if (!loginWindows_->count()) {
- connect(tabs, SIGNAL(onTitleChanged(const QString&)), this, SLOT(handleTabsTitleChanged(const QString&)));
- }
loginWindows_->addWidget(loginWindow);
tabs_->addWidget(tabs);
- list_->addItem(QString("Account %1").arg(loginWindows_->count()));
+ std::string account = QString("Account %1").arg(loginWindows_->count()).toStdString();
+ accountData_.addAccount(account);
+ emit serverListModel_->layoutChanged();
}
-void QtSingleWindow::handleListItemClicked(QListWidgetItem* /*item*/) {
- //FIXME: Should use a full model/view and do this properly (and render pretty things ourselves too)
+void QtSingleWindow::handleListItemClicked(const QModelIndex& item) {
auto currentTabs = tabs_->widget(tabs_->currentIndex());
disconnect(currentTabs, SIGNAL(onTitleChanged(const QString&)), this, SLOT(handleTabsTitleChanged(const QString&)));
- loginWindows_->setCurrentIndex(list_->currentRow());
- tabs_->setCurrentIndex(list_->currentRow());
+ loginWindows_->setCurrentIndex(item.row());
+ tabs_->setCurrentIndex(item.row());
currentTabs = tabs_->widget(tabs_->currentIndex());
connect(currentTabs, SIGNAL(onTitleChanged(const QString&)), this, SLOT(handleTabsTitleChanged(const QString&)));
//TODO change the title of the window.
handleTabsTitleChanged(QString("Swift"));
+
}
}
diff --git a/Swift/QtUI/QtSingleWindow.h b/Swift/QtUI/QtSingleWindow.h
index 9a7e475..a707cd3 100644
--- a/Swift/QtUI/QtSingleWindow.h
+++ b/Swift/QtUI/QtSingleWindow.h
@@ -10,11 +10,14 @@
#include <QSplitter>
#include <QStackedWidget>
+#include <Swift/QtUI/ServerList/ServerListModel.h>
namespace Swift {
class QtChatTabs;
class QtLoginWindow;
class QtSettingsProvider;
+ class QtServerListView;
+ class ServerListModel;
class QtSingleWindow : public QSplitter {
Q_OBJECT
@@ -32,7 +35,7 @@ namespace Swift {
private slots:
void handleSplitterMoved();
void handleTabsTitleChanged(const QString& title);
- void handleListItemClicked(QListWidgetItem*);
+ void handleListItemClicked(const QModelIndex&);
private:
void handleGeometryChanged();
void restoreSplitters();
@@ -40,7 +43,9 @@ namespace Swift {
private:
QtSettingsProvider* settings_;
- QListWidget* list_;
+ SwiftAccountData accountData_;
+ QtServerListView* serverList_;
+ ServerListModel* serverListModel_;
QStackedWidget* loginWindows_;
QStackedWidget* tabs_;
};
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index 29aa8b8..535ccaf 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -137,6 +137,9 @@ sources = [
"MUCSearch/MUCSearchServiceItem.cpp",
"MUCSearch/QtLeafSortFilterProxyModel.cpp",
"MUCSearch/QtMUCSearchWindow.cpp",
+ "ServerList/ServerListDelegate.cpp",
+ "ServerList/ServerListModel.cpp",
+ "ServerList/QtServerListView.cpp",
"qrc_DefaultTheme.cc",
"qrc_Swift.cc",
"QtAboutWidget.cpp",
diff --git a/Swift/QtUI/ServerList/QtServerListView.cpp b/Swift/QtUI/ServerList/QtServerListView.cpp
new file mode 100644
index 0000000..c22680f
--- /dev/null
+++ b/Swift/QtUI/ServerList/QtServerListView.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/QtUI/ServerList/QtServerListView.h>
+
+namespace Swift {
+
+QtServerListView::QtServerListView() {
+ QPalette newPalette = palette();
+ //TODO move color and theme variables to a shared location.
+ newPalette.setColor(QPalette::Base, { 38, 81, 112 });
+ setAutoFillBackground(true);
+ setPalette(newPalette);
+ delegate_ = std::make_unique<ServerListDelegate>();
+ setItemDelegate(delegate_.get());
+ setMaximumWidth(widgetWidth);
+ setMinimumWidth(widgetWidth);
+ setFrameStyle(QFrame::NoFrame);
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setSelectionMode(QAbstractItemView::NoSelection);
+}
+
+QtServerListView::~QtServerListView() {
+
+}
+
+}
diff --git a/Swift/QtUI/ServerList/QtServerListView.h b/Swift/QtUI/ServerList/QtServerListView.h
new file mode 100644
index 0000000..bd625aa
--- /dev/null
+++ b/Swift/QtUI/ServerList/QtServerListView.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <QListView>
+
+#include <Swift/QtUI/ServerList/ServerListDelegate.h>
+
+namespace Swift {
+ class QtServerListView : public QListView {
+ Q_OBJECT
+ public:
+ QtServerListView();
+ virtual ~QtServerListView();
+ private:
+ std::unique_ptr<ServerListDelegate> delegate_;
+ static const int widgetWidth = 82;
+ };
+}
diff --git a/Swift/QtUI/ServerList/ServerListDelegate.cpp b/Swift/QtUI/ServerList/ServerListDelegate.cpp
new file mode 100644
index 0000000..2afb4ea
--- /dev/null
+++ b/Swift/QtUI/ServerList/ServerListDelegate.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/QtUI/ServerList/ServerListDelegate.h>
+
+#include <QApplication>
+#include <QBitmap>
+#include <QBrush>
+#include <QColor>
+#include <QDebug>
+#include <QFileInfo>
+#include <QFontMetrics>
+#include <QPainter>
+#include <QPainterPath>
+#include <QPen>
+#include <QPolygon>
+
+#include <Swift/QtUI/QtScaledAvatarCache.h>
+#include <Swift/QtUI/ServerList/ServerListModel.h>
+
+namespace Swift {
+
+ServerListDelegate::ServerListDelegate() {
+
+}
+
+ServerListDelegate::~ServerListDelegate() {
+
+}
+
+void ServerListDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
+ QColor bgColor(38, 81, 112);
+ painter->fillRect(option.rect, bgColor);
+ SwiftAccountData::SwiftAccountDataItem* item = static_cast<SwiftAccountData::SwiftAccountDataItem*>(index.internalPointer());
+ paintServerAvatar(painter, option, item->iconPath_, item->status_, false, item->unreadCount_);
+}
+
+QSize ServerListDelegate::sizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/) const {
+ //TODO Make this configurable.
+ return QSize(75, 75);
+}
+
+void ServerListDelegate::paintServerAvatar(QPainter* painter, const QStyleOptionViewItem& option, const QString& avatarPath, const StatusShow& /*serverPresence*/, bool isIdle, size_t unreadCount) const {
+ painter->save();
+ QRect fullRegion(option.rect);
+ if (option.state & QStyle::State_Selected) {
+ painter->fillRect(fullRegion, option.palette.highlight());
+ painter->setPen(option.palette.highlightedText().color());
+ }
+ auto secondLineColor = painter->pen().color();
+ secondLineColor.setAlphaF(0.7);
+
+ QRect presenceRegion(QPoint(common_.farLeftMargin, fullRegion.top() + common_.horizontalMargin), QSize(presenceIconWidth, presenceIconHeight));
+ QRect idleIconRegion(QPoint(common_.farLeftMargin, fullRegion.top()), QSize(presenceIconWidth * 2, presenceIconHeight - common_.verticalMargin));
+ int calculatedAvatarSize = fullRegion.height() - common_.verticalMargin;
+ //This overlaps the presenceIcon, so must be painted first
+ QRect avatarRegion(QPoint(presenceRegion.right() - common_.presenceIconWidth / 2, presenceRegion.top()), QSize(calculatedAvatarSize, calculatedAvatarSize));
+
+ QPixmap avatarPixmap;
+ if (!avatarPath.isEmpty()) {
+ QString scaledAvatarPath = QtScaledAvatarCache(avatarRegion.height()).getScaledAvatarPath(avatarPath);
+ if (QFileInfo(scaledAvatarPath).exists()) {
+ avatarPixmap.load(scaledAvatarPath);
+ }
+ }
+ if (avatarPixmap.isNull()) {
+ avatarPixmap = QPixmap(":/icons/avatar.svg").scaled(avatarRegion.height(), avatarRegion.width(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ }
+ painter->drawPixmap(avatarRegion.topLeft() + QPoint(((avatarRegion.width() - avatarPixmap.width()) / 2), (avatarRegion.height() - avatarPixmap.height()) / 2), avatarPixmap);
+ //Paint the presence status over the top of the avatar
+ //FIXME enable drawing status when ServerPresence data are available.
+ /*{
+ //TODO make the colors consistent with chattables work from QtChatOverviewDelegate::paint, copying for now
+ const auto green = QColor(124, 243, 145);
+ const auto yellow = QColor(243, 243, 0);
+ const auto red = QColor(255, 45, 71);
+ const auto grey = QColor(159, 159, 159);
+ QColor color = grey;
+ switch (serverPresence.getType()) {
+ case StatusShow::Online: color = green; break;
+ case StatusShow::FFC: color = green; break;
+ case StatusShow::Away: color = yellow; break;
+ case StatusShow::XA: color = yellow; break;
+ case StatusShow::DND: color = red; break;
+ case StatusShow::None: color = grey; break;
+ }
+ QPen pen(color);
+ pen.setWidth(1);
+ painter->setRenderHint(QPainter::Antialiasing, true);
+ painter->setPen(pen);
+ painter->setBrush(QBrush(color, Qt::SolidPattern));
+ painter->drawEllipse(presenceRegion);
+ }*/
+
+ if (isIdle) {
+ common_.idleIcon.paint(painter, idleIconRegion, Qt::AlignBottom | Qt::AlignHCenter);
+ }
+
+ if (unreadCount > 0) {
+ QRect unreadRect(avatarRegion.right() - common_.unreadCountSize - common_.horizontalMargin, avatarRegion.top(), common_.unreadCountSize, common_.unreadCountSize);
+ QPen pen(QColor("black"));
+ pen.setWidth(1);
+ painter->setRenderHint(QPainter::Antialiasing, true);
+ painter->setPen(pen);
+ painter->setBrush(QBrush(QColor("red"), Qt::SolidPattern));
+ painter->drawEllipse(unreadRect);
+ painter->setBackgroundMode(Qt::TransparentMode);
+ painter->setPen(QColor("white"));
+ common_.drawElidedText(painter, unreadRect, QString("%1").arg(unreadCount), Qt::AlignCenter);
+ }
+ painter->restore();
+}
+
+}
diff --git a/Swift/QtUI/ServerList/ServerListDelegate.h b/Swift/QtUI/ServerList/ServerListDelegate.h
new file mode 100644
index 0000000..79afc37
--- /dev/null
+++ b/Swift/QtUI/ServerList/ServerListDelegate.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <QStyledItemDelegate>
+
+#include <Swiften/Elements/StatusShow.h>
+
+#include <Swift/QtUI/Roster/DelegateCommons.h>
+
+namespace Swift {
+
+ class ServerListDelegate : public QStyledItemDelegate {
+ public:
+ ServerListDelegate();
+ ~ServerListDelegate();
+ QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
+ void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
+ private:
+ void paintServerAvatar(QPainter* painter, const QStyleOptionViewItem& option, const QString& avatarPath, const StatusShow& presence, bool isIdle, size_t unreadCount) const;
+ private:
+ DelegateCommons common_;
+ static const int presenceIconHeight = 12;
+ static const int presenceIconWidth = 12;
+ };
+
+}
diff --git a/Swift/QtUI/ServerList/ServerListModel.cpp b/Swift/QtUI/ServerList/ServerListModel.cpp
new file mode 100644
index 0000000..e5dc35e
--- /dev/null
+++ b/Swift/QtUI/ServerList/ServerListModel.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+//#include <Swift/QtUI/ServerList/ServerListModel.h>
+#include <QBrush>
+#include <QColor>
+#include <QMimeData>
+
+#include "ServerListModel.h"
+
+namespace Swift {
+
+ServerListModel::ServerListModel() {
+}
+
+ServerListModel::~ServerListModel() {
+}
+
+QVariant ServerListModel::data(const QModelIndex& index, int role) const {
+ if (!index.isValid()) {
+ return QVariant();
+ }
+ SwiftAccountData::SwiftAccountDataItem* item = static_cast<SwiftAccountData::SwiftAccountDataItem*>(index.internalPointer());
+ switch (role) {
+ case Qt::DisplayRole: return QString(item->userJID_.toBare().toString().c_str());
+ case Qt::BackgroundRole: return QBrush(Qt::transparent);
+ case Qt::ToolTipRole: return QString(item->userJID_.toBare().toString().c_str());
+ default: return QVariant();
+ }
+}
+
+QModelIndex ServerListModel::index(int row, int column, const QModelIndex& /*parent*/) const {
+ if (!modelData_ || static_cast<size_t>(row) >= modelData_->size()) {
+ return QModelIndex();
+ }
+ return createIndex(row, column, modelData_->getAccount(row));
+}
+
+QModelIndex ServerListModel::parent(const QModelIndex& /*index*/) const {
+ return QModelIndex();
+}
+
+QMimeData* ServerListModel::mimeData(const QModelIndexList& indexes) const {
+ return QAbstractItemModel::mimeData(indexes);
+}
+
+int ServerListModel::rowCount(const QModelIndex& /*parent*/) const {
+ if (!modelData_) {
+ return 0;
+ }
+ return modelData_->size();
+}
+
+int ServerListModel::columnCount(const QModelIndex& /*parent*/) const {
+ if (!modelData_) {
+ return 0;
+ }
+ return 1;
+}
+
+void ServerListModel::handleDataChanged() {
+ emit layoutChanged();
+}
+
+}
diff --git a/Swift/QtUI/ServerList/ServerListModel.h b/Swift/QtUI/ServerList/ServerListModel.h
new file mode 100644
index 0000000..86541a0
--- /dev/null
+++ b/Swift/QtUI/ServerList/ServerListModel.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <boost/signals2.hpp>
+
+#include <QAbstractItemModel>
+
+#include <Swiften/Elements/StatusShow.h>
+#include <Swiften/JID/JID.h>
+
+namespace Swift {
+
+ class SwiftAccountData {
+ public:
+ struct SwiftAccountDataItem {
+ SwiftAccountDataItem(std::string serverID) : serverID_(serverID) {}
+ //FIXME eliminate serverID_, the userJID_ will be the ID when we connect with the actual data.
+ std::string serverID_;
+ QString iconPath_;
+ JID userJID_;
+ size_t unreadCount_ = 0;
+ StatusShow status_ = StatusShow::None;
+ boost::signals2::scoped_connection dataChangedConnection_;
+ boost::signals2::signal<void ()> onDataChanged;
+ void handleChangeStatusRequest(StatusShow::Type show, const std::string& /*statusText*/) {
+ status_ = show;
+ onDataChanged();
+ }
+ };
+ public:
+ SwiftAccountData() {}
+ ~SwiftAccountData() {
+ for (auto account : accounts_) {
+ delete account;
+ }
+ }
+ //FIXME make addAccount with SwiftAccountDataItem, and added after a succesfull connection to the server has been established.
+ void addAccount(JID userJID) {
+ SwiftAccountDataItem* newItem = new SwiftAccountDataItem(userJID);
+ newItem->dataChangedConnection_ = newItem->onDataChanged.connect(boost::bind(&SwiftAccountData::handleDataChanged, this));
+ accounts_.push_back(newItem);
+ }
+ SwiftAccountDataItem* getAccount(int index) {
+ if (index >= accounts_.size()) {
+ return nullptr;
+ }
+ return accounts_[index];
+ }
+ size_t size() {
+ return accounts_.size();
+ }
+ public:
+ boost::signals2::signal<void()> onDataChanged;
+ private:
+ void handleDataChanged() {
+ onDataChanged();
+ }
+ private:
+ QList<SwiftAccountDataItem*> accounts_;
+ };
+
+ class ServerListModel : public QAbstractItemModel {
+ Q_OBJECT
+ public:
+ ServerListModel();
+ ~ServerListModel();
+
+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
+ QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
+ QModelIndex parent(const QModelIndex& index) const override;
+
+ QMimeData* mimeData(const QModelIndexList& indexes) const override;
+
+ virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override;
+ virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override;
+
+ void setModelData(SwiftAccountData* data) { modelData_ = data; }
+ void handleDataChanged();
+ private:
+ SwiftAccountData* modelData_;
+ };
+}