Support Qt 5.11 and fix image provider.

This commit is contained in:
Black Hat 2018-07-07 17:38:20 +08:00
parent a850224c98
commit 17fa7cc7da
24 changed files with 666 additions and 808 deletions

View File

@ -3,6 +3,7 @@
#include <QNetworkProxy> #include <QNetworkProxy>
#include <QQmlContext> #include <QQmlContext>
#include "room.h"
#include "matrix/controller.h" #include "matrix/controller.h"
#include "matrix/roomlistmodel.h" #include "matrix/roomlistmodel.h"
#include "matrix/imageprovider.h" #include "matrix/imageprovider.h"
@ -19,11 +20,13 @@ int main(int argc, char *argv[])
QGuiApplication app(argc, argv); QGuiApplication app(argc, argv);
// Enable this if you need proxy. // Enable this if you need proxy.
QNetworkProxy proxy; // QNetworkProxy proxy;
proxy.setType(QNetworkProxy::HttpProxy); // proxy.setType(QNetworkProxy::HttpProxy);
proxy.setHostName("localhost"); // proxy.setHostName("localhost");
proxy.setPort(1082); // proxy.setPort(1082);
QNetworkProxy::setApplicationProxy(proxy); // QNetworkProxy::setApplicationProxy(proxy);
qmlRegisterType<Room>(); qRegisterMetaType<Room*> ("Room*");
qmlRegisterType<Controller>("Matrique", 0, 1, "Controller"); qmlRegisterType<Controller>("Matrique", 0, 1, "Controller");
qmlRegisterType<RoomListModel>("Matrique", 0, 1, "RoomListModel"); qmlRegisterType<RoomListModel>("Matrique", 0, 1, "RoomListModel");
@ -31,13 +34,12 @@ int main(int argc, char *argv[])
QQmlApplicationEngine engine; QQmlApplicationEngine engine;
Connection* m_connection = new Connection();
ImageProvider* m_provider = new ImageProvider(); ImageProvider* m_provider = new ImageProvider();
m_provider->setConnection(m_connection);
engine.rootContext()->setContextProperty("m_connection", m_connection); engine.rootContext()->setContextProperty("imageProvider", m_provider->getConnection());
engine.addImageProvider(QLatin1String("mxc"), m_provider); engine.addImageProvider(QLatin1String("mxc"), m_provider);
engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml"))); engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml")));
if (engine.rootObjects().isEmpty()) if (engine.rootObjects().isEmpty())
return -1; return -1;

View File

@ -18,8 +18,7 @@ SOURCES += main.cpp \
matrix/controller.cpp \ matrix/controller.cpp \
matrix/roomlistmodel.cpp \ matrix/roomlistmodel.cpp \
matrix/imageprovider.cpp \ matrix/imageprovider.cpp \
matrix/messageeventmodel.cpp \ matrix/messageeventmodel.cpp
matrix/matriqueroom.cpp
RESOURCES += \ RESOURCES += \
res.qrc res.qrc
@ -53,5 +52,4 @@ HEADERS += \
matrix/controller.h \ matrix/controller.h \
matrix/roomlistmodel.h \ matrix/roomlistmodel.h \
matrix/imageprovider.h \ matrix/imageprovider.h \
matrix/messageeventmodel.h \ matrix/messageeventmodel.h
matrix/matriqueroom.h

View File

@ -3,51 +3,52 @@
#include "libqmatrixclient/connection.h" #include "libqmatrixclient/connection.h"
Controller::Controller(QObject *parent) : QObject(parent) { Controller::Controller(QObject *parent) : QObject(parent) {
connect(m_connection, &QMatrixClient::Connection::connected, this, &Controller::connected);
connect(m_connection, &QMatrixClient::Connection::resolveError, this, &Controller::reconnect);
connect(m_connection, &QMatrixClient::Connection::syncError, this, &Controller::reconnect);
connect(m_connection, &QMatrixClient::Connection::syncDone, this, &Controller::resync);
} }
Controller::~Controller() { Controller::~Controller() {
} }
void Controller::login(QString home, QString user, QString pass) { void Controller::login() {
if (!isLogin) { if (!isLogin) {
if(home.isEmpty()) home = "matrix.org";
qDebug() << "UserID:" << userID; qDebug() << "UserID:" << userID;
qDebug() << "Token:" << token; qDebug() << "Token:" << token;
qDebug() << "Home:" << home;
m_connection->setHomeserver(QUrl(homeserver));
m_connection->connectWithToken(userID, token, "");
}
}
void Controller::loginWithCredentials(QString serverAddr, QString user, QString pass) {
if(!isLogin) {
qDebug() << "Server:" << serverAddr;
qDebug() << "User:" << user; qDebug() << "User:" << user;
qDebug() << "Pass:" << pass; qDebug() << "Pass:" << pass;
if(!userID.isEmpty() && !token.isEmpty()) { if(!user.isEmpty() && !pass.isEmpty()) {
qDebug() << "Using token.";
m_connection->connectWithToken(userID, token, "");
} else if(!user.isEmpty() && !pass.isEmpty()) {
qDebug() << "Using given credential."; qDebug() << "Using given credential.";
m_connection->connectToServer("@"+user+":"+home, pass, ""); m_connection->setHomeserver(QUrl(serverAddr));
m_connection->connectToServer(user, pass, "");
} }
} else { } else {
qDebug() << "You are already logged in."; qDebug() << "You are already logged in.";
} }
} }
void Controller::setConnection(QMatrixClient::Connection* conn) {
m_connection = conn;
connect(m_connection, &QMatrixClient::Connection::connected, this, &Controller::connected);
connect(m_connection, &QMatrixClient::Connection::resolveError, this, &Controller::reconnect);
connect(m_connection, &QMatrixClient::Connection::syncError, this, &Controller::reconnect);
connect(m_connection, &QMatrixClient::Connection::syncDone, this, &Controller::resync);
emit connectionChanged();
}
void Controller::logout() { void Controller::logout() {
userID = ""; qDebug() << "Logging out.";
token = ""; setUserID("");
setToken("");
setIsLogin(false); setIsLogin(false);
} }
void Controller::connected() { void Controller::connected() {
qDebug() << "Logged in.";
setHomeserver(m_connection->homeserver().toString());
setUserID(m_connection->userId()); setUserID(m_connection->userId());
setToken(m_connection->accessToken()); setToken(m_connection->accessToken());
m_connection->loadState(); m_connection->loadState();

View File

@ -2,9 +2,7 @@
#define CONTROLLER_H #define CONTROLLER_H
#include <QObject> #include <QObject>
#include "libqmatrixclient/connection.h" #include "libqmatrixclient/connection.h"
#include "roomlistmodel.h" #include "roomlistmodel.h"
namespace QMatrixClient { namespace QMatrixClient {
@ -15,25 +13,27 @@ class Controller : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QMatrixClient::Connection *connection READ getConnection WRITE setConnection NOTIFY connectionChanged) Q_PROPERTY(QMatrixClient::Connection* connection READ getConnection NOTIFY connectionChanged)
Q_PROPERTY(bool isLogin READ getIsLogin WRITE setIsLogin NOTIFY isLoginChanged) Q_PROPERTY(bool isLogin READ getIsLogin WRITE setIsLogin NOTIFY isLoginChanged)
Q_PROPERTY(QString homeserver READ getHomeserver WRITE setHomeserver NOTIFY homeserverChanged)
Q_PROPERTY(QString userID READ getUserID WRITE setUserID NOTIFY userIDChanged) Q_PROPERTY(QString userID READ getUserID WRITE setUserID NOTIFY userIDChanged)
Q_PROPERTY(QByteArray token READ getToken WRITE setToken NOTIFY tokenChanged) Q_PROPERTY(QByteArray token READ getToken WRITE setToken NOTIFY tokenChanged)
Q_PROPERTY(bool busy READ getBusy WRITE setBusy NOTIFY busyChanged)
public: public:
explicit Controller(QObject *parent = nullptr); explicit Controller(QObject *parent = nullptr);
~Controller(); ~Controller();
// All the Q_INVOKABLEs. // All the Q_INVOKABLEs.
Q_INVOKABLE void login(QString, QString, QString); Q_INVOKABLE void login();
Q_INVOKABLE void loginWithCredentials(QString, QString, QString);
Q_INVOKABLE void logout(); Q_INVOKABLE void logout();
// All the non-Q_INVOKABLE functions. // All the non-Q_INVOKABLE functions.
// All the Q_PROPERTYs. // All the Q_PROPERTYs.
QMatrixClient::Connection* m_connection; QMatrixClient::Connection* m_connection = new QMatrixClient::Connection();
QMatrixClient::Connection* getConnection() { return m_connection; } QMatrixClient::Connection* getConnection() { return m_connection; }
void setConnection(QMatrixClient::Connection* conn);
bool isLogin = false; bool isLogin = false;
bool getIsLogin() { return isLogin; } bool getIsLogin() { return isLogin; }
@ -62,6 +62,24 @@ class Controller : public QObject
} }
} }
QString homeserver;
QString getHomeserver() { return homeserver; }
void setHomeserver(QString n) {
if (n != homeserver) {
homeserver = n;
emit homeserverChanged();
}
}
bool busy = false;
bool getBusy() { return busy; }
void setBusy(bool b) {
if (b != busy) {
busy = b;
emit busyChanged();
}
}
private: private:
void connected(); void connected();
void resync(); void resync();
@ -72,7 +90,9 @@ class Controller : public QObject
void isLoginChanged(); void isLoginChanged();
void userIDChanged(); void userIDChanged();
void tokenChanged(); void tokenChanged();
void homeServerChanged(); void homeserverChanged();
void busyChanged();
void errorOccured();
public slots: public slots:
}; };

View File

@ -10,6 +10,14 @@
using QMatrixClient::MediaThumbnailJob; using QMatrixClient::MediaThumbnailJob;
ImageProviderConnection::ImageProviderConnection(QObject* parent) : QObject(parent) {
}
ImageProviderConnection::~ImageProviderConnection() {
}
ImageProvider::ImageProvider(QObject* parent) ImageProvider::ImageProvider(QObject* parent)
: QQuickImageProvider(QQmlImageProviderBase::Image, : QQuickImageProvider(QQmlImageProviderBase::Image,
QQmlImageProviderBase::ForceAsynchronousImageLoading) QQmlImageProviderBase::ForceAsynchronousImageLoading)
@ -17,6 +25,7 @@ ImageProvider::ImageProvider(QObject *parent)
#if (QT_VERSION < QT_VERSION_CHECK(5, 10, 0)) #if (QT_VERSION < QT_VERSION_CHECK(5, 10, 0))
qRegisterMetaType<MediaThumbnailJob*>(); qRegisterMetaType<MediaThumbnailJob*>();
#endif #endif
m_connection = new ImageProviderConnection();
} }
QImage ImageProvider::requestImage(const QString& id, QImage ImageProvider::requestImage(const QString& id,
@ -34,9 +43,10 @@ QImage ImageProvider::requestImage(const QString& id,
MediaThumbnailJob* job = nullptr; MediaThumbnailJob* job = nullptr;
QReadLocker locker(&m_lock); QReadLocker locker(&m_lock);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
QMetaObject::invokeMethod(m_connection, QMetaObject::invokeMethod(m_connection,
[=] { return m_connection->getThumbnail(mxcUri, requestedSize); }, [=] { return m_connection->getConnection()->getThumbnail(mxcUri, requestedSize); },
Qt::BlockingQueuedConnection, &job); Qt::BlockingQueuedConnection, &job);
#else #else
QMetaObject::invokeMethod(m_connection, "getThumbnail", QMetaObject::invokeMethod(m_connection, "getThumbnail",
@ -64,9 +74,3 @@ QImage ImageProvider::requestImage(const QString& id,
return result; return result;
} }
void ImageProvider::setConnection(QMatrixClient::Connection* connection)
{
QWriteLocker locker(&m_lock);
m_connection = connection;
}

View File

@ -3,10 +3,31 @@
#include <QtQuick/QQuickImageProvider> #include <QtQuick/QQuickImageProvider>
#include <QtCore/QReadWriteLock> #include <QtCore/QReadWriteLock>
#include <QObject>
namespace QMatrixClient { #include "libqmatrixclient/connection.h"
class Connection;
class ImageProviderConnection: public QObject
{
Q_OBJECT
Q_PROPERTY(QMatrixClient::Connection* connection READ getConnection WRITE setConnection NOTIFY connectionChanged)
public:
explicit ImageProviderConnection(QObject* parent = nullptr);
~ImageProviderConnection();
QMatrixClient::Connection* getConnection() { return m_connection; }
Q_INVOKABLE void setConnection(QMatrixClient::Connection* connection) {
qDebug() << "Connection changed.";
emit connectionChanged();
m_connection = connection;
} }
private:
QMatrixClient::Connection* m_connection;
signals:
void connectionChanged();
};
class ImageProvider: public QQuickImageProvider class ImageProvider: public QQuickImageProvider
{ {
@ -16,13 +37,13 @@ class ImageProvider: public QQuickImageProvider
QImage requestImage(const QString& id, QSize* pSize, QImage requestImage(const QString& id, QSize* pSize,
const QSize& requestedSize) override; const QSize& requestedSize) override;
void setConnection(QMatrixClient::Connection* connection);
void initializeEngine(QQmlEngine *engine, const char *uri); void initializeEngine(QQmlEngine *engine, const char *uri);
ImageProviderConnection* getConnection() { return m_connection; }
private: private:
QMatrixClient::Connection* m_connection;
QReadWriteLock m_lock; QReadWriteLock m_lock;
ImageProviderConnection* m_connection;
}; };
#endif // IMAGEPROVIDER_H #endif // IMAGEPROVIDER_H

View File

@ -23,7 +23,6 @@ QHash<int, QByteArray> MessageEventModel::roleNames() const
roles[AuthorRole] = "author"; roles[AuthorRole] = "author";
roles[ContentRole] = "content"; roles[ContentRole] = "content";
roles[ContentTypeRole] = "contentType"; roles[ContentTypeRole] = "contentType";
roles[HighlightRole] = "highlight";
roles[ReadMarkerRole] = "readMarker"; roles[ReadMarkerRole] = "readMarker";
roles[SpecialMarksRole] = "marks"; roles[SpecialMarksRole] = "marks";
roles[LongOperationRole] = "progressInfo"; roles[LongOperationRole] = "progressInfo";
@ -38,7 +37,7 @@ MessageEventModel::MessageEventModel(QObject* parent)
qRegisterMetaType<QMatrixClient::FileTransferInfo>(); qRegisterMetaType<QMatrixClient::FileTransferInfo>();
} }
void MessageEventModel::changeRoom(MatriqueRoom* room) void MessageEventModel::changeRoom(QMatrixClient::Room* room)
{ {
if (room == m_currentRoom) if (room == m_currentRoom)
return; return;
@ -114,7 +113,7 @@ inline bool hasValidTimestamp(const QMatrixClient::TimelineItem& ti)
return ti->timestamp().isValid(); return ti->timestamp().isValid();
} }
QDateTime MessageEventModel::makeMessageTimestamp(MatriqueRoom::rev_iter_t baseIt) const QDateTime MessageEventModel::makeMessageTimestamp(QMatrixClient::Room::rev_iter_t baseIt) const
{ {
const auto& timeline = m_currentRoom->messageEvents(); const auto& timeline = m_currentRoom->messageEvents();
auto ts = baseIt->event()->timestamp(); auto ts = baseIt->event()->timestamp();
@ -137,7 +136,7 @@ QDateTime MessageEventModel::makeMessageTimestamp(MatriqueRoom::rev_iter_t baseI
return {}; return {};
} }
QString MessageEventModel::makeDateString(MatriqueRoom::rev_iter_t baseIt) const QString MessageEventModel::makeDateString(QMatrixClient::Room::rev_iter_t baseIt) const
{ {
auto date = makeMessageTimestamp(baseIt).toLocalTime().date(); auto date = makeMessageTimestamp(baseIt).toLocalTime().date();
if (QMatrixClient::SettingsGroup("UI") if (QMatrixClient::SettingsGroup("UI")
@ -402,9 +401,6 @@ QVariant MessageEventModel::data(const QModelIndex& index, int role) const
} }
} }
if(role == HighlightRole)
return m_currentRoom->isEventHighlighted(event);
if(role == ReadMarkerRole) if(role == ReadMarkerRole)
return event->id() == lastReadEventId; return event->id() == lastReadEventId;

View File

@ -2,8 +2,7 @@
#define MESSAGEEVENTMODEL_H #define MESSAGEEVENTMODEL_H
#include <QtCore/QAbstractListModel> #include <QtCore/QAbstractListModel>
#include "room.h"
#include "matriqueroom.h"
class MessageEventModel: public QAbstractListModel class MessageEventModel: public QAbstractListModel
{ {
@ -13,7 +12,7 @@ class MessageEventModel: public QAbstractListModel
// has to be re-calculated anyway). // has to be re-calculated anyway).
// XXX: A better way would be to make [Room::]Timeline a list model // XXX: A better way would be to make [Room::]Timeline a list model
// itself, leaving only representation of the model to a client. // itself, leaving only representation of the model to a client.
Q_PROPERTY(MatriqueRoom* room MEMBER m_currentRoom CONSTANT) Q_PROPERTY(QMatrixClient::Room* room MEMBER m_currentRoom CONSTANT)
public: public:
enum EventRoles { enum EventRoles {
@ -25,7 +24,6 @@ class MessageEventModel: public QAbstractListModel
AuthorRole, AuthorRole,
ContentRole, ContentRole,
ContentTypeRole, ContentTypeRole,
HighlightRole,
ReadMarkerRole, ReadMarkerRole,
SpecialMarksRole, SpecialMarksRole,
LongOperationRole, LongOperationRole,
@ -33,7 +31,7 @@ class MessageEventModel: public QAbstractListModel
explicit MessageEventModel(QObject* parent = nullptr); explicit MessageEventModel(QObject* parent = nullptr);
void changeRoom(MatriqueRoom* room); void changeRoom(QMatrixClient::Room* room);
int rowCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
@ -43,11 +41,11 @@ class MessageEventModel: public QAbstractListModel
void refreshEvent(const QString& eventId); void refreshEvent(const QString& eventId);
private: private:
MatriqueRoom* m_currentRoom; QMatrixClient::Room* m_currentRoom;
QString lastReadEventId; QString lastReadEventId;
QDateTime makeMessageTimestamp(MatriqueRoom::rev_iter_t baseIt) const; QDateTime makeMessageTimestamp(QMatrixClient::Room::rev_iter_t baseIt) const;
QString makeDateString(MatriqueRoom::rev_iter_t baseIt) const; QString makeDateString(QMatrixClient::Room::rev_iter_t baseIt) const;
void refreshEventRoles(const QString& eventId, const QVector<int> roles); void refreshEventRoles(const QString& eventId, const QVector<int> roles);
}; };

View File

@ -1,131 +1,43 @@
#include "roomlistmodel.h" #include "roomlistmodel.h"
#include <QtGui/QIcon> #include <QtGui/QBrush>
#include <QtGui/QColor>
#include "matriqueroom.h" #include <QtCore/QDebug>
#include "connection.h"
#include "user.h"
RoomListModel::RoomListModel(QObject* parent) RoomListModel::RoomListModel(QObject* parent)
: QAbstractListModel(parent) : QAbstractListModel(parent)
{ } {
m_connection = 0;
}
RoomListModel::~RoomListModel()
{
}
void RoomListModel::setConnection(QMatrixClient::Connection* connection) void RoomListModel::setConnection(QMatrixClient::Connection* connection)
{ {
Q_ASSERT(connection);
using QMatrixClient::Room;
beginResetModel(); beginResetModel();
m_connection = connection; m_connection = connection;
connect( connection, &QMatrixClient::Connection::loggedOut, m_rooms.clear();
this, [=]{ deleteConnection(connection); } ); connect( connection, &QMatrixClient::Connection::newRoom, this, &RoomListModel::addRoom );
connect( connection, &QMatrixClient::Connection::invitedRoom, for( QMatrixClient::Room* room: connection->roomMap().values() ) {
this, &RoomListModel::updateRoom); connect( room, &QMatrixClient::Room::namesChanged, this, &RoomListModel::namesChanged );
connect( connection, &QMatrixClient::Connection::joinedRoom, m_rooms.append(room);
this, &RoomListModel::updateRoom); }
connect( connection, &QMatrixClient::Connection::leftRoom,
this, &RoomListModel::updateRoom);
connect( connection, &QMatrixClient::Connection::aboutToDeleteRoom,
this, &RoomListModel::deleteRoom);
for( auto r: connection->roomMap() )
doAddRoom(r);
endResetModel(); endResetModel();
} }
void RoomListModel::deleteConnection(QMatrixClient::Connection* connection) { QMatrixClient::Room* RoomListModel::roomAt(int row)
{
return m_rooms.at(row);
} }
MatriqueRoom* RoomListModel::roomAt(QModelIndex index) const void RoomListModel::addRoom(QMatrixClient::Room* room)
{
return m_rooms.at(index.row());
}
QModelIndex RoomListModel::indexOf(MatriqueRoom* room) const
{
return index(m_rooms.indexOf(room), 0);
}
void RoomListModel::updateRoom(QMatrixClient::Room* room,
QMatrixClient::Room* prev)
{
// There are two cases when this method is called:
// 1. (prev == nullptr) adding a new room to the room list
// 2. (prev != nullptr) accepting/rejecting an invitation or inviting to
// the previously left room (in both cases prev has the previous state).
if (prev == room)
{
qCritical() << "RoomListModel::updateRoom: room tried to replace itself";
refresh(static_cast<MatriqueRoom*>(room));
return;
}
if (prev && room->id() != prev->id())
{
qCritical() << "RoomListModel::updateRoom: attempt to update room"
<< room->id() << "to" << prev->id();
// That doesn't look right but technically we still can do it.
}
// Ok, we're through with pre-checks, now for the real thing.
auto* newRoom = static_cast<MatriqueRoom*>(room);
const auto it = std::find_if(m_rooms.begin(), m_rooms.end(),
[=](const MatriqueRoom* r) { return r == prev || r == newRoom; });
if (it != m_rooms.end())
{
const int row = it - m_rooms.begin();
// There's no guarantee that prev != newRoom
if (*it == prev && *it != newRoom)
{
prev->disconnect(this);
m_rooms.replace(row, newRoom);
connectRoomSignals(newRoom);
}
emit dataChanged(index(row), index(row));
}
else
{ {
beginInsertRows(QModelIndex(), m_rooms.count(), m_rooms.count()); beginInsertRows(QModelIndex(), m_rooms.count(), m_rooms.count());
doAddRoom(newRoom); connect( room, &QMatrixClient::Room::namesChanged, this, &RoomListModel::namesChanged );
endInsertRows();
}
}
void RoomListModel::deleteRoom(QMatrixClient::Room* room)
{
auto i = m_rooms.indexOf(static_cast<MatriqueRoom*>(room));
if (i == -1)
return; // Already deleted, nothing to do
beginRemoveRows(QModelIndex(), i, i);
m_rooms.removeAt(i);
endRemoveRows();
}
void RoomListModel::doAddRoom(QMatrixClient::Room* r)
{
if (auto* room = static_cast<MatriqueRoom*>(r))
{
m_rooms.append(room); m_rooms.append(room);
connectRoomSignals(room); endInsertRows();
} else
{
qCritical() << "Attempt to add nullptr to the room list";
Q_ASSERT(false);
}
}
void RoomListModel::connectRoomSignals(MatriqueRoom* room)
{
connect(room, &MatriqueRoom::displaynameChanged,
this, [=]{ displaynameChanged(room); } );
connect( room, &MatriqueRoom::unreadMessagesChanged,
this, [=]{ unreadMessagesChanged(room); } );
connect( room, &MatriqueRoom::notificationCountChanged,
this, [=]{ unreadMessagesChanged(room); } );
connect( room, &MatriqueRoom::joinStateChanged,
this, [=]{ refresh(room); });
connect( room, &MatriqueRoom::avatarChanged,
this, [=]{ refresh(room, { Qt::DecorationRole }); });
} }
int RoomListModel::rowCount(const QModelIndex& parent) const int RoomListModel::rowCount(const QModelIndex& parent) const
@ -145,83 +57,47 @@ QVariant RoomListModel::data(const QModelIndex& index, int role) const
qDebug() << "UserListModel: something wrong here..."; qDebug() << "UserListModel: something wrong here...";
return QVariant(); return QVariant();
} }
auto room = m_rooms.at(index.row()); QMatrixClient::Room* room = m_rooms.at(index.row());
using QMatrixClient::JoinState; if( role == Qt::DisplayRole )
switch (role)
{ {
case Qt::DisplayRole:
return room->displayName(); return room->displayName();
case Qt::DecorationRole: }
if( role == Qt::ForegroundRole )
{
if( room->highlightCount() > 0 )
return QBrush(QColor("orange"));
return QVariant();
}
if( role == Qt::DecorationRole )
{ {
if(room->avatarUrl().toString() != "") { if(room->avatarUrl().toString() != "") {
qInfo() << "Room avatar:" << room->avatarUrl();
return room->avatarUrl(); return room->avatarUrl();
} else if(room->users().length() == 2) {
QMatrixClient::User* user = room->users().at(0);
qInfo() << "User avatar:" << user->avatarUrl();
return user->avatarUrl();
} }
return QVariant();
} }
case Qt::StatusTipRole: if (role == Qt::StatusTipRole )
{ {
return room->topic(); return room->topic();
} }
case Qt::ToolTipRole:
{
int hlCount = room->highlightCount();
auto result = QStringLiteral("<b>%1</b><br>").arg(room->displayName());
result += tr("Main alias: %1<br>").arg(room->canonicalAlias());
result += tr("Members: %1<br>").arg(room->memberCount());
if (hlCount > 0)
result += tr("Unread mentions: %1<br>").arg(hlCount);
result += tr("ID: %1<br>").arg(room->id());
switch (room->joinState())
{
case JoinState::Join:
result += tr("You joined this room");
break;
case JoinState::Leave:
result += tr("You left this room");
break;
default:
result += tr("You were invited into this room");
}
return result;
}
case HasUnreadRole:
return room->hasUnreadMessages();
case HighlightCountRole:
return room->highlightCount();
case JoinStateRole:
return toCString(room->joinState()); // FIXME: better make the enum QVariant-convertible
default:
return QVariant(); return QVariant();
} }
void RoomListModel::namesChanged(QMatrixClient::Room* room)
{
int row = m_rooms.indexOf(room);
emit dataChanged(index(row), index(row));
}
void RoomListModel::unreadMessagesChanged(QMatrixClient::Room* room)
{
int row = m_rooms.indexOf(room);
emit dataChanged(index(row), index(row));
} }
QHash<int, QByteArray> RoomListModel::roleNames() const { QHash<int, QByteArray> RoomListModel::roleNames() const {
QHash<int, QByteArray> roles; QHash<int, QByteArray> roles;
roles[Qt::DisplayRole] = "name"; roles[Qt::DisplayRole] = "name";
roles[Qt::DecorationRole] = "avatar"; roles[Qt::DecorationRole] = "avatar";
roles[Qt::StatusTipRole] = "value"; roles[Qt::StatusTipRole] = "topic";
return roles; return roles;
} }
void RoomListModel::displaynameChanged(MatriqueRoom* room)
{
refresh(room);
}
void RoomListModel::unreadMessagesChanged(MatriqueRoom* room)
{
refresh(room);
}
void RoomListModel::refresh(MatriqueRoom* room, const QVector<int>& roles)
{
int row = m_rooms.indexOf(room);
if (row == -1)
qCritical() << "Room" << room->id() << "not found in the room list";
else
emit dataChanged(index(row), index(row), roles);
}

View File

@ -2,55 +2,39 @@
#define ROOMLISTMODEL_H #define ROOMLISTMODEL_H
#include <QtCore/QAbstractListModel> #include <QtCore/QAbstractListModel>
#include "room.h"
#include "matriqueroom.h" #include "connection.h"
namespace QMatrixClient
{
class Connection;
class Room;
}
class RoomListModel: public QAbstractListModel class RoomListModel: public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QMatrixClient::Connection *connection READ getConnection WRITE setConnection) Q_PROPERTY(QMatrixClient::Connection *connection READ getConnection WRITE setConnection)
public: public:
enum Roles { RoomListModel(QObject* parent=0);
HasUnreadRole = Qt::UserRole + 1, virtual ~RoomListModel();
HighlightCountRole, JoinStateRole
};
explicit RoomListModel(QObject* parent = nullptr);
QMatrixClient::Connection* getConnection() {return m_connection;} QMatrixClient::Connection* getConnection() {return m_connection;}
void setConnection(QMatrixClient::Connection* connection); void setConnection(QMatrixClient::Connection* connection);
void deleteConnection(QMatrixClient::Connection* connection);
MatriqueRoom* roomAt(QModelIndex index) const; Q_INVOKABLE QMatrixClient::Room* roomAt(int row);
QModelIndex indexOf(MatriqueRoom* room) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
Q_INVOKABLE int rowCount(const QModelIndex& parent=QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role) const override;
QHash<int, QByteArray> roleNames() const; QHash<int, QByteArray> roleNames() const;
int rowCount(const QModelIndex& parent) const override;
private slots: private slots:
void displaynameChanged(MatriqueRoom* room); void namesChanged(QMatrixClient::Room* room);
void unreadMessagesChanged(MatriqueRoom* room); void unreadMessagesChanged(QMatrixClient::Room* room);
void refresh(MatriqueRoom* room, const QVector<int>& roles = {}); void addRoom(QMatrixClient::Room* room);
void updateRoom(QMatrixClient::Room* room,
QMatrixClient::Room* prev);
void deleteRoom(QMatrixClient::Room* room);
private: private:
QMatrixClient::Connection* m_connection; QMatrixClient::Connection* m_connection;
QList<MatriqueRoom*> m_rooms; QList<QMatrixClient::Room*> m_rooms;
void doAddRoom(QMatrixClient::Room* r); signals:
void connectRoomSignals(MatriqueRoom* room); void connectionChanged();
}; };
#endif // ROOMLISTMODEL_H #endif // ROOMLISTMODEL_H

View File

@ -1,20 +0,0 @@
import QtQuick 2.10
import QtQuick.Controls 2.3
import "qrc:/qml/form"
Page {
property var contactListModel
ListForm {
id: roomListForm
height: parent.height
width: 320
listModel: contactListModel
}
DetailForm {
id: detailForm
anchors.fill: parent
anchors.leftMargin: roomListForm.width
}
}

View File

@ -9,18 +9,6 @@ import "qrc:/qml/component"
Page { Page {
property var controller property var controller
property alias homeserver: settings.server
property alias username: settings.user
property alias password: settings.pass
Settings {
id: settings
property alias server: serverField.text
property alias user: usernameField.text
property alias pass: passwordField.text
}
Row { Row {
anchors.fill: parent anchors.fill: parent
@ -68,29 +56,33 @@ Page {
height: parent.height height: parent.height
padding: 64 padding: 64
Column { ColumnLayout {
id: main_col id: mainCol
spacing: 8 width: parent.width
anchors.fill: parent
ImageStatus { ImageStatus {
width: 96 Layout.preferredWidth: 96
height: width Layout.preferredHeight: 96
Layout.alignment: Qt.AlignHCenter
source: "qrc:/asset/img/avatar.png" source: "qrc:/asset/img/avatar.png"
anchors.horizontalCenter: parent.horizontalCenter
} }
TextField { TextField {
id: serverField id: serverField
width: parent.width
height: 48 Layout.fillWidth: true
placeholderText: "Server"
leftPadding: 16 leftPadding: 16
topPadding: 0 topPadding: 0
bottomPadding: 0 bottomPadding: 0
placeholderText: "Server"
background: Rectangle { background: Rectangle {
color: "#eaeaea" implicitHeight: 48
color: Material.theme == Material.Light ? "#eaeaea" : "#242424"
border.color: parent.activeFocus ? Material.accent : "transparent" border.color: parent.activeFocus ? Material.accent : "transparent"
border.width: 2 border.width: 2
} }
@ -98,15 +90,19 @@ Page {
TextField { TextField {
id: usernameField id: usernameField
width: parent.width
height: 48 Layout.fillWidth: true
placeholderText: "Username"
leftPadding: 16 leftPadding: 16
topPadding: 0 topPadding: 0
bottomPadding: 0 bottomPadding: 0
placeholderText: "Username"
background: Rectangle { background: Rectangle {
color: "#eaeaea" implicitHeight: 48
color: Material.theme == Material.Light ? "#eaeaea" : "#242424"
border.color: parent.activeFocus ? Material.accent : "transparent" border.color: parent.activeFocus ? Material.accent : "transparent"
border.width: 2 border.width: 2
} }
@ -114,15 +110,20 @@ Page {
TextField { TextField {
id: passwordField id: passwordField
width: parent.width
height: 48 Layout.fillWidth: true
placeholderText: "Password"
leftPadding: 16 leftPadding: 16
topPadding: 0 topPadding: 0
bottomPadding: 0 bottomPadding: 0
placeholderText: "Password"
echoMode: TextInput.Password
background: Rectangle { background: Rectangle {
color: "#eaeaea" implicitHeight: 48
color: Material.theme == Material.Light ? "#eaeaea" : "#242424"
border.color: parent.activeFocus ? Material.accent : "transparent" border.color: parent.activeFocus ? Material.accent : "transparent"
border.width: 2 border.width: 2
} }
@ -130,20 +131,13 @@ Page {
Button { Button {
id: loginButton id: loginButton
Layout.fillWidth: true
text: "LOGIN" text: "LOGIN"
highlighted: true highlighted: true
width: parent.width
onClicked: controller.login(homeserver, username, password) onClicked: controller.loginWithCredentials(serverField.text, usernameField.text, passwordField.text)
}
Button {
id: logoutButton
text: "LOGOUT"
flat: true
width: parent.width
onClicked: controller.logout()
} }
} }
} }

View File

@ -1,24 +1,47 @@
import QtQuick 2.10 import QtQuick 2.10
import QtQuick.Controls 2.3 import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
import Matrique 0.1
import "qrc:/qml/form" import "qrc:/qml/form"
import Matrique 0.1
Page { Page {
property RoomListModel roomListModel property alias connection: roomListModel.connection
id: page
RoomListModel {
id: roomListModel
}
RowLayout {
anchors.fill: parent
spacing: 0
ListForm { ListForm {
id: roomListForm id: roomListForm
height: parent.height
width: 320 Layout.fillHeight: true
// Layout.preferredWidth: {
// if (page.width > 560) {
// return page.width * 0.4;
// } else {
// return 80;
// }
// }
Layout.preferredWidth: 320
Layout.maximumWidth: 360
listModel: roomListModel listModel: roomListModel
} }
RoomForm { RoomForm {
id: roomForm id: roomForm
anchors.fill: parent
anchors.leftMargin: roomListForm.width Layout.fillWidth: true
roomIndex: roomListForm.currentIndex Layout.fillHeight: true
currentRoom: roomListForm.currentIndex != -1 ? roomListModel.roomAt(roomListForm.currentIndex) : null
}
} }
} }

View File

@ -1,58 +0,0 @@
import QtQuick 2.10
import QtQuick.Controls 2.3
import QtQuick.Controls.Material 2.3
Page {
property alias theme: themeSwitch.checked
header: TabBar {
id: settingBar
width: parent.width
z: 10
currentIndex: settingBar.currentIndex
TabButton {
text: qsTr("Overview")
}
TabButton {
text: qsTr("Interface")
}
TabButton {
text: qsTr("Network")
}
TabButton {
text: qsTr("Sync")
}
}
SwipeView {
id: settingSwipe
currentIndex: settingBar.currentIndex
anchors.fill: parent
Page {
}
Page {
Column {
width: parent.width
Switch {
id: themeSwitch
text: qsTr("Dark Theme")
}
}
}
Page {
}
Page {
}
}
}

View File

@ -1,76 +0,0 @@
import QtQuick 2.10
import QtQuick.Controls 2.3
Rectangle {
id: root
property Connection currentConnection: null
property var currentRoom: null
function setRoom(room) {
currentRoom = room
messageModel.changeRoom(room)
}
function setConnection(conn) {
currentConnection = conn
messageModel.setConnection(conn)
}
function sendLine(text) {
if(!currentRoom || !currentConnection) return
currentConnection.postMessage(currentRoom, "m.text", text)
}
ListView {
id: chatView
anchors.fill: parent
flickableDirection: Flickable.VerticalFlick
verticalLayoutDirection: ListView.BottomToTop
model: MessageEventModel { id: messageModel }
delegate: Row {
id: message
width: parent.width
spacing: 8
Label {
id: timelabel
text: time.toLocaleTimeString("hh:mm:ss")
color: "grey"
}
Label {
width: 64
elide: Text.ElideRight
text: eventType == "message" ? author : "***"
color: eventType == "message" ? "grey" : "lightgrey"
horizontalAlignment: Text.AlignRight
}
Label {
text: content
wrapMode: Text.Wrap
width: parent.width - (x - parent.x) - spacing
color: eventType == "message" ? "black" : "lightgrey"
}
}
section {
property: "date"
labelPositioning: ViewSection.CurrentLabelAtStart
delegate: Rectangle {
width: parent.width
height: childrenRect.height
Label {
width: parent.width
text: section.toLocaleString(Qt.locale())
color: "grey"
horizontalAlignment: Text.AlignRight
}
}
}
onAtYBeginningChanged: {
if(currentRoom && atYBeginning) currentRoom.getPreviousContent()
}
}
}

View File

@ -1,30 +1,31 @@
import QtQuick 2.10 import QtQuick 2.11
import QtQuick.Controls 2.3 import QtQuick.Controls 2.4
import QtGraphicalEffects 1.0 import QtGraphicalEffects 1.0
Item { Item {
property bool statusIndicator: false
property bool opaqueBackground: false property bool opaqueBackground: false
property alias source: avatar.source property alias source: avatar.source
id: item
Rectangle { Rectangle {
width: parent.width width: item.width
height: parent.width height: item.width
radius: parent.width / 2 radius: item.width / 2
color: "white" color: "white"
visible: opaqueBackground visible: opaqueBackground
} }
Image { Image {
id: avatar id: avatar
width: parent.width width: item.width
height: parent.width height: item.width
mipmap: true mipmap: true
layer.enabled: true layer.enabled: true
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
sourceSize.width: parent.width sourceSize.width: item.width
sourceSize.height: parent.width sourceSize.height: item.width
layer.effect: OpacityMask { layer.effect: OpacityMask {
maskSource: Item { maskSource: Item {
@ -38,15 +39,5 @@ Item {
} }
} }
} }
Rectangle {
width: parent.width
height: parent.width
radius: parent.width / 2
color: "transparent"
border.color: "#4caf50"
border.width: 4
visible: statusIndicator
}
} }
} }

View File

@ -1,10 +1,11 @@
import QtQuick 2.10 import QtQuick 2.10
import QtQuick.Controls 2.3 import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3
import QtQuick.Controls.Material 2.3
Item { Item {
property alias icon: iconText.text property alias icon: iconText.text
property var color: "white" property var color: Material.theme == Material.Light ? "black" : "white"
id: item id: item

View File

@ -3,15 +3,9 @@ import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3
import QtQuick.Controls.Material 2.3 import QtQuick.Controls.Material 2.3
Drawer { Item {
property SwipeView swipeView Rectangle {
anchors.fill: parent
interactive: false
position: 1.0
visible: true
modal: false
background: Rectangle {
color: Material.accent color: Material.accent
} }
} }

View File

@ -4,7 +4,7 @@ import QtQuick.Layouts 1.3
import QtQuick.Controls.Material 2.3 import QtQuick.Controls.Material 2.3
Item { Item {
property Item page property var page
property alias contentItem: buttonDelegate.contentItem property alias contentItem: buttonDelegate.contentItem
signal clicked signal clicked

View File

@ -50,28 +50,14 @@ Item {
width: parent.height width: parent.height
height: parent.height height: parent.height
contentItem: Text { contentItem: MaterialIcon { icon: "\ue0b7" }
text: "\ue0b7"
font.pointSize: 16
font.family: materialFont.name
color: "white"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
} }
ItemDelegate { ItemDelegate {
width: parent.height width: parent.height
height: parent.height height: parent.height
contentItem: Text { contentItem: MaterialIcon { icon: "\ue62e" }
text: "\ue62e"
font.pointSize: 16
font.family: materialFont.name
color: "white"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
} }
} }
} }

View File

@ -1,13 +1,97 @@
import QtQuick 2.10 import QtQuick 2.11
import QtQuick.Controls 2.3 import QtQuick.Controls 2.4
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.11
import QtGraphicalEffects 1.0 import QtGraphicalEffects 1.0
import QtQuick.Controls.Material 2.3 import QtQuick.Controls.Material 2.3
import QtQml.Models 2.3
import Matrique 0.1
import "qrc:/qml/component" import "qrc:/qml/component"
Item { Item {
property var listModel property alias listModel: delegateModel.model
property alias currentIndex: listView.currentIndex property alias currentIndex: listView.currentIndex
readonly property bool mini: width <= 80 // Used as an indicator of whether the listform should be displayed as "Mini mode".
DelegateModel {
id: delegateModel
groups: [
DelegateModelGroup {
name: "filterGroup"; includeByDefault: true
}
]
filterOnGroup: "filterGroup"
delegate: ItemDelegate {
width: parent.width
height: 80
onClicked: listView.currentIndex = index
contentItem: RowLayout {
anchors.fill: parent
anchors.margins: 16
spacing: 16
ImageStatus {
Layout.preferredWidth: height
Layout.fillHeight: true
source: avatar == null || avatar == "" ? "qrc:/asset/img/avatar.png" : "image://mxc/" + avatar
opaqueBackground: true
}
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter
Label {
Layout.fillWidth: true
Layout.fillHeight: true
text: {
if (name != "") {
return name;
}
if (alias != "") {
return alias;
}
return id
}
font.pointSize: 16
elide: Text.ElideRight
wrapMode: Text.NoWrap
}
Label {
Layout.fillWidth: true
Layout.fillHeight: true
text: topic === "" ? "No topic yet." : topic
elide: Text.ElideRight
wrapMode: Text.NoWrap
}
}
}
}
function applyFilter(filterName){
var roomCount = listModel.rowCount();
for (var i = 0; i < roomCount; i++){
var roomName = "";
if (listModel.roomAt(i).name != "") {
roomName = listModel.roomAt(i).name;
} else if (model.alias != "") {
roomName = listModel.roomAt(i).alias;
} else {
roomName = listModel.roomAt(i).id;
}
if (roomName.toLowerCase().indexOf(filterName.toLowerCase()) !== -1) {
items.addGroups(i, 1, "filterGroup");
} else {items.removeGroups(i, 1, "filterGroup");}
}
}
}
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
@ -22,10 +106,10 @@ Item {
} }
TextField { TextField {
id: serverField id: searchField
width: parent.width width: parent.width
height: 36 height: 36
leftPadding: 16 leftPadding: mini ? 4 : 16
topPadding: 0 topPadding: 0
bottomPadding: 0 bottomPadding: 0
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
@ -34,19 +118,17 @@ Item {
Row { Row {
anchors.fill: parent anchors.fill: parent
Text { MaterialIcon {
width: parent.height icon: "\ue8b6"
height: parent.height
text: "\ue8b6"
font.pointSize: 16
font.family: materialFont.name
color: "white" color: "white"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter width: mini ? parent.width : parent.height
height: parent.height
} }
Text { Text {
height: parent.height height: parent.height
visible: !mini
text: "Search" text: "Search"
color: "white" color: "white"
font.pointSize: 12 font.pointSize: 12
@ -56,15 +138,19 @@ Item {
} }
Rectangle { Rectangle {
width: serverField.activeFocus || serverField.text != "" ? parent.width : 0 width: searchField.activeFocus || searchField.text != "" ? parent.width : 0
height: parent.height height: parent.height
color: "white" color: "white"
Behavior on width { Behavior on width {
PropertyAnimation { easing.type: Easing.InOutQuad; duration: 200 } PropertyAnimation { easing.type: Easing.InOutCubic; duration: 200 }
} }
} }
} }
onTextChanged: {
delegateModel.applyFilter(text);
}
} }
} }
@ -77,13 +163,12 @@ Item {
anchors.fill: parent anchors.fill: parent
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
color: "#eaeaea" color: Material.theme == Material.Light ? "#eaeaea" : "#242424"
} }
Text { Label {
z: 10 z: 10
text: "Here? No, not here." text: mini ? "Empty" : "Here? No, not here."
color: "#424242"
anchors.centerIn: parent anchors.centerIn: parent
visible: listView.count === 0 visible: listView.count === 0
} }
@ -94,53 +179,15 @@ Item {
width: parent.width width: parent.width
height: parent.height height: parent.height
model: listModel
highlight: Rectangle { highlight: Rectangle {
color: Material.accent color: Material.accent
opacity: 0.2 opacity: 0.2
} }
highlightMoveDuration: 250
ScrollBar.vertical: ScrollBar { id: scrollBar } ScrollBar.vertical: ScrollBar { id: scrollBar }
delegate: ItemDelegate { model: delegateModel
width: parent.width
height: 80
onClicked: listView.currentIndex = index
contentItem: Row {
width: parent.width - 32
height: parent.height - 32
spacing: 16
ImageStatus {
width: parent.height
height: parent.height
source: avatar == "" ? "qrc:/asset/img/avatar.png" : "image://mxc/" + avatar
opaqueBackground: true
}
Column {
width: parent.width - parent.height - parent.spacing
height: parent.height
Text {
width: parent.width
text: name
color: "#424242"
font.pointSize: 16
elide: Text.ElideRight
wrapMode: Text.NoWrap
}
Text {
width: parent.width
text: value
color: "#424242"
elide: Text.ElideRight
wrapMode: Text.NoWrap
}
}
}
}
} }
} }
} }

View File

@ -3,15 +3,37 @@ import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3
import QtQuick.Controls.Material 2.3 import QtQuick.Controls.Material 2.3
import QtGraphicalEffects 1.0 import QtGraphicalEffects 1.0
import Matrique 0.1
import "qrc:/qml/component" import "qrc:/qml/component"
Item { Item {
property int roomIndex id: item
property var currentRoom
Pane {
anchors.fill: parent
padding: 0
background: Item {
anchors.fill: parent
visible: currentRoom == null
Pane {
anchors.fill: parent
}
Label {
z: 10
text: "Please choose a room."
anchors.centerIn: parent
}
}
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
spacing: 0 spacing: 0
visible: currentRoom != null
Pane { Pane {
z: 10 z: 10
padding: 16 padding: 16
@ -20,37 +42,89 @@ Item {
Layout.preferredHeight: 80 Layout.preferredHeight: 80
background: Rectangle { background: Rectangle {
color: "#eaeaea" color: Material.theme == Material.Light ? "#eaeaea" : "#242424"
} }
Row { RowLayout {
anchors.fill: parent anchors.fill: parent
spacing: 16 spacing: 16
ImageStatus { ImageStatus {
width: parent.height Layout.preferredWidth: parent.height
height: parent.height Layout.fillHeight: true
source: "qrc:/asset/img/avatar.png" source: "qrc:/asset/img/avatar.png"
} }
Column { ColumnLayout {
height: parent.height Layout.fillWidth: true
Text { Layout.fillHeight: true
text: "Astolfo"
font.pointSize: 18 Label {
color: "#424242" Layout.fillWidth: true
text: currentRoom != null ? currentRoom.name : ""
font.pointSize: 16
elide: Text.ElideRight
wrapMode: Text.NoWrap
} }
Text {
text: "Rider of Black" Label {
color: "#424242" Layout.fillWidth: true
text: currentRoom != null ? currentRoom.topic : ""
elide: Text.ElideRight
wrapMode: Text.NoWrap
} }
} }
} }
} }
Pane { ListView {
id: messageListView
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
Layout.leftMargin: 16
Layout.rightMargin: 16
displayMarginBeginning: 40
displayMarginEnd: 40
verticalLayoutDirection: ListView.BottomToTop
spacing: 12
// model: MessageEventModel{ currentRoom: item.currentRoom }
model: 10
delegate: Row {
readonly property bool sentByMe: index % 2 == 0
id: messageRow
height: 40
anchors.right: sentByMe ? parent.right : undefined
spacing: 6
Rectangle {
id: avatar
width: height
height: parent.height
color: "grey"
visible: !sentByMe
}
Rectangle {
width: Math.min(messageText.implicitWidth + 24,
messageListView.width - (!sentByMe ? avatar.width + messageRow.spacing : 0))
height: parent.height
color: sentByMe ? "lightgrey" : Material.accent
Label {
id: messageText
text: index
color: sentByMe ? "black" : "white"
anchors.fill: parent
anchors.margins: 12
wrapMode: Label.Wrap
}
}
}
ScrollBar.vertical: ScrollBar { /*anchors.left: messageListView.right*/ }
} }
Pane { Pane {
@ -68,14 +142,7 @@ Item {
Layout.preferredWidth: height Layout.preferredWidth: height
Layout.fillHeight: true Layout.fillHeight: true
contentItem: Text { contentItem: MaterialIcon { icon: "\ue226" }
text: "\ue226"
// color: "#424242"
font.pointSize: 16
font.family: materialFont.name
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
} }
TextField { TextField {
@ -85,9 +152,10 @@ Item {
leftPadding: 16 leftPadding: 16
topPadding: 0 topPadding: 0
bottomPadding: 0 bottomPadding: 0
selectByMouse: true
background: Rectangle { background: Rectangle {
color: "#eaeaea" color: Material.theme == Material.Light ? "#eaeaea" : "#242424"
} }
} }
@ -95,31 +163,11 @@ Item {
Layout.preferredWidth: height Layout.preferredWidth: height
Layout.fillHeight: true Layout.fillHeight: true
contentItem: Text { contentItem: MaterialIcon { icon: "\ue24e" }
text: "\ue24e"
// color: parent.pressed ? Material.accent : "#424242"
font.pointSize: 16
font.family: materialFont.name
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
background: Rectangle { background: Rectangle {
color: "#eaeaea" color: Material.theme == Material.Light ? "#eaeaea" : "#242424"
} }
}
ItemDelegate {
Layout.preferredWidth: height
Layout.fillHeight: true
contentItem: Text {
text: "\ue163"
// color: "#424242"
font.pointSize: 16
font.family: materialFont.name
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
} }
} }
} }

View File

@ -1,65 +1,128 @@
import QtQuick 2.10 import QtQuick 2.11
import QtQuick.Controls 2.3 import QtQuick.Controls 2.4
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.4
import QtQuick.Controls.Material 2.2 import QtQuick.Controls.Material 2.4
import QtGraphicalEffects 1.0 import QtGraphicalEffects 1.0
import Qt.labs.settings 1.0 import Qt.labs.settings 1.0
import Qt.labs.platform 1.0 as Platform
import "qrc:/qml/component"
import "qrc:/qml/form"
import Matrique 0.1 import Matrique 0.1
import "component"
import "form"
ApplicationWindow { ApplicationWindow {
id: window id: window
visible: true visible: true
width: 960 width: 960
height: 640 height: 640
minimumWidth: 320
minimumHeight: 320
title: qsTr("Matrique") title: qsTr("Matrique")
Material.theme: settingPage.theme ? Material.Dark : Material.Light
Controller {
id: matrixController
connection: m_connection
}
RoomListModel {
id: roomListModel
connection: m_connection
}
Settings {
id: settings
property alias userID: matrixController.userID
property alias token: matrixController.token
}
FontLoader { id: materialFont; source: "qrc:/asset/font/material.ttf" } FontLoader { id: materialFont; source: "qrc:/asset/font/material.ttf" }
Settings {
id: setting
property alias homeserver: matriqueController.homeserver
property alias userID: matriqueController.userID
property alias token: matriqueController.token
}
// Platform.SystemTrayIcon {
// visible: true
// iconSource: "qrc:/asset/img/icon.png"
// onActivated: {
// window.show()
// window.raise()
// window.requestActivate()
// }
// }
Controller {
id: matriqueController
onErrorOccured: {
errorDialog.text = err;
errorDialog.open();
}
}
Popup {
property bool busy: matriqueController.busy
id: busyPopup
x: (window.width - width) / 2
y: (window.height - height) / 2
modal: true
focus: true
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
BusyIndicator { running: true }
onBusyChanged: {
if(busyPopup.busy) { busyPopup.open(); }
else { busyPopup.close(); }
}
}
Dialog {
property alias text: errorLabel.text
id: errorDialog
width: 360
modal: true
title: "ERROR"
x: (window.width - width) / 2
y: (window.height - height) / 2
standardButtons: Dialog.Ok
Label {
id: errorLabel
width: parent.width
text: "Label"
wrapMode: Text.Wrap
}
}
Component {
id: loginPage
Login { controller: matriqueController }
}
Room {
id: roomPage
connection: matriqueController.connection
}
RowLayout {
anchors.fill: parent
spacing: 0
SideNav { SideNav {
id: sideNav id: sideNav
width: 80 Layout.preferredWidth: 80
height: window.height Layout.fillHeight: true
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
spacing: 0 spacing: 0
SideNavButton { SideNavButton {
id: statusNavButton
contentItem: ImageStatus { contentItem: ImageStatus {
width: parent.width anchors.fill: parent
height: parent.width anchors.margins: 15
source: "qrc:/asset/img/avatar.png" source: "qrc:/asset/img/avatar.png"
anchors.horizontalCenter: parent.horizontalCenter
statusIndicator: true
opaqueBackground: false opaqueBackground: false
} }
page: Room { page: roomPage
id: roomPage
roomListModel: roomListModel
}
} }
Rectangle { Rectangle {
@ -68,62 +131,13 @@ ApplicationWindow {
} }
SideNavButton { SideNavButton {
contentItem: Text { contentItem: MaterialIcon { icon: "\ue8b8"; color: "white" }
text: "\ue853"
font.pointSize: 16
font.family: materialFont.name
color: "white"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
page: Login { onClicked: matriqueController.logout()
id: loginPage
controller: matrixController
}
} }
SideNavButton { SideNavButton {
contentItem: Text { contentItem: MaterialIcon { icon: "\ue879"; color: "white" }
text: "\ue5d2"
font.pointSize: 16
font.family: materialFont.name
color: "white"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
page: Contact {
id: contactPage
contactListModel: roomListModel
}
}
SideNavButton {
contentItem: Text {
text: "\ue8b8"
font.pointSize: 16
font.family: materialFont.name
color: "white"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
page: Setting {
id: settingPage
}
}
SideNavButton {
contentItem: Text {
text: "\ue879"
font.pointSize: 16
font.family: materialFont.name
color: "white"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
onClicked: Qt.quit() onClicked: Qt.quit()
} }
} }
@ -131,8 +145,23 @@ ApplicationWindow {
StackView { StackView {
id: stackView id: stackView
anchors.fill: parent
anchors.leftMargin: sideNav.width
initialItem: roomPage initialItem: roomPage
Layout.fillWidth: true
Layout.fillHeight: true
}
}
Component.onCompleted: {
imageProvider.setConnection(matriqueController.connection)
imageProvider.connection = matriqueController.connection
console.log(matriqueController.homeserver, matriqueController.userID, matriqueController.token)
if (matriqueController.userID != "" && matriqueController.token != "") {
console.log("Perform auto-login.");
matriqueController.login();
} else {
stackView.replace(loginPage);
}
} }
} }

View File

@ -6,7 +6,6 @@
<file>asset/font/material.ttf</file> <file>asset/font/material.ttf</file>
<file>qml/Login.qml</file> <file>qml/Login.qml</file>
<file>qml/main.qml</file> <file>qml/main.qml</file>
<file>qml/Setting.qml</file>
<file>qml/component/ButtonDelegate.qml</file> <file>qml/component/ButtonDelegate.qml</file>
<file>qml/component/ImageStatus.qml</file> <file>qml/component/ImageStatus.qml</file>
<file>qml/component/SideNav.qml</file> <file>qml/component/SideNav.qml</file>
@ -14,8 +13,8 @@
<file>qml/Room.qml</file> <file>qml/Room.qml</file>
<file>qml/form/DetailForm.qml</file> <file>qml/form/DetailForm.qml</file>
<file>qml/form/ListForm.qml</file> <file>qml/form/ListForm.qml</file>
<file>qml/Contact.qml</file>
<file>qml/component/ChatRoom.qml</file>
<file>qml/component/SideNavButton.qml</file> <file>qml/component/SideNavButton.qml</file>
<file>qml/component/MaterialIcon.qml</file>
<file>asset/img/icon.png</file>
</qresource> </qresource>
</RCC> </RCC>