diff --git a/CMakeLists.txt b/CMakeLists.txt
index 771481f..47599dc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -12,6 +12,7 @@ include(GNUInstallDirs)
include_directories(.)
find_package(Qt5Widgets CONFIG REQUIRED)
+find_package(Qt5QuickCompiler CONFIG REQUIRED)
find_package(Qt5LinguistTools)
if(NOT CMAKE_BUILD_TYPE)
diff --git a/main.cpp b/main.cpp
index 4c4b3ea..ee65d0a 100644
--- a/main.cpp
+++ b/main.cpp
@@ -77,6 +77,11 @@ int main(int argc, char *argv[])
new Shared::Global(); //translates enums
+ // QtQuickControls2 Style
+ if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) {
+ qputenv("QT_QUICK_CONTROLS_STYLE", "Material");
+ }
+
Squawk w;
w.show();
diff --git a/resources/qml/feed.qml b/resources/qml/feed.qml
new file mode 100644
index 0000000..3fc0ba8
--- /dev/null
+++ b/resources/qml/feed.qml
@@ -0,0 +1,79 @@
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Layouts 1.14
+
+ListView {
+ id: list
+ verticalLayoutDirection: ListView.BottomToTop
+
+ required model
+
+ delegate: RowLayout {
+ id: root
+ width: ListView.view.width
+
+ // placeholder
+ Item {
+ Layout.preferredWidth: root.layoutDirection === Qt.LeftToRight ? 5 : 10
+ }
+
+// Avatar {
+// id: avatar
+// visible: !sentByMe
+// avatarUrl: root.avatarUrl
+// Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
+// name: root.senderName
+// Layout.preferredHeight: Kirigami.Units.gridUnit * 2.2
+// Layout.preferredWidth: Kirigami.Units.gridUnit * 2.2
+// }
+
+ Item {
+ Layout.preferredWidth: content.width + 16
+ Layout.preferredHeight: content.height + 16
+
+ Rectangle {
+ id: messageBubble
+ anchors.fill: parent
+
+ color: "blue"
+ }
+
+ ColumnLayout {
+ id: content
+ spacing: 5
+ anchors.centerIn: parent
+
+ Label {
+ text: model.sender
+ }
+
+ Label {
+ text: model.text
+ wrapMode: Text.Wrap
+ Layout.maximumWidth: root.width
+ }
+
+ // message meta data: date, deliveryState
+ RowLayout {
+ Layout.bottomMargin: -4
+
+ Label {
+ id: dateLabel
+ text: model.date
+ }
+
+// Icon {
+// source: "edit-symbolic"
+// visible: model.correction
+// Layout.preferredHeight: 10
+// Layout.preferredWidth: 10
+// }
+ }
+ }
+ }
+
+ Item {
+ Layout.fillWidth: true
+ }
+ }
+}
diff --git a/resources/resources.qrc b/resources/resources.qrc
index 4fb3e5b..179b251 100644
--- a/resources/resources.qrc
+++ b/resources/resources.qrc
@@ -160,5 +160,7 @@
images/fallback/light/small/favorite.svg
images/fallback/light/small/unfavorite.svg
images/fallback/light/small/add.svg
+
+ qml/feed.qml
diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt
index 0ed806f..4fada80 100644
--- a/ui/CMakeLists.txt
+++ b/ui/CMakeLists.txt
@@ -7,8 +7,7 @@ set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
# Find the QtWidgets library
-find_package(Qt5Widgets CONFIG REQUIRED)
-find_package(Qt5DBus CONFIG REQUIRED)
+find_package(Qt5 CONFIG REQUIRED COMPONENTS Widgets DBus Core)
find_package(Boost 1.36.0 CONFIG REQUIRED)
if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})
diff --git a/ui/models/messagefeed.cpp b/ui/models/messagefeed.cpp
index bd86b67..a097693 100644
--- a/ui/models/messagefeed.cpp
+++ b/ui/models/messagefeed.cpp
@@ -20,6 +20,15 @@
#include
+const QHash MessageFeed::roles = {
+ {Text, "text"},
+ {Sender, "sender"},
+ {Date, "date"},
+ {DeliveryState, "deliveryState"},
+ {Correction, "correction"},
+ {SentByMe,"sentByMe"}
+};
+
MessageFeed::MessageFeed(QObject* parent):
QAbstractListModel(parent),
storage(),
@@ -69,25 +78,43 @@ void MessageFeed::removeMessage(const QString& id)
QVariant MessageFeed::data(const QModelIndex& index, int role) const
{
int i = index.row();
- if (syncState == syncing) {
- --i;
- }
QVariant answer;
- switch (role) {
- case Qt::DisplayRole: {
- if (i == -1) {
- return "Loading...";
- }
-
- StorageByTime::const_iterator itr = indexByTime.nth(i);
- if (itr != indexByTime.end()) {
- const Shared::Message* msg = *itr;
- answer = msg->getFrom() + ": " + msg->getBody();
- }
+
+ StorageByTime::const_iterator itr = indexByTime.nth(i);
+ if (itr != indexByTime.end()) {
+ const Shared::Message* msg = *itr;
+
+ switch (role) {
+ case Text:
+ answer = msg->getBody();
+ break;
+ case Sender:
+ answer = msg->getFrom();
+ break;
+ case Date:
+ answer = msg->getTime();
+ break;
+ case DeliveryState:
+ answer = static_cast(msg->getState());
+ break;
+ case Correction:
+ answer = msg->getEdited();
+ break;
+ case SentByMe:
+ answer = msg->getOutgoing();
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch (role) {
+ case Text:
+ answer = "loading...";
+ break;
+ default:
+ answer = "";
+ break;
}
- break;
- default:
- break;
}
return answer;
@@ -115,27 +142,28 @@ bool MessageFeed::canFetchMore(const QModelIndex& parent) const
void MessageFeed::fetchMore(const QModelIndex& parent)
{
if (syncState == incomplete) {
- beginInsertRows(QModelIndex(), 0, 0);
+ beginInsertRows(QModelIndex(), storage.size(), storage.size());
syncState = syncing;
endInsertRows();
if (storage.size() == 0) {
emit requestArchive("");
} else {
- emit requestArchive((*indexByTime.nth(0))->getId());
+ emit requestArchive((*indexByTime.rbegin())->getId());
}
}
}
void MessageFeed::responseArchive(const std::list list)
{
+ Storage::size_type size = storage.size();
if (syncState == syncing) {
- beginRemoveRows(QModelIndex(), 0, 0);
+ beginRemoveRows(QModelIndex(), size, size);
syncState = incomplete;
endRemoveRows();
}
- beginInsertRows(QModelIndex(), 0, list.size() - 1);
+ beginInsertRows(QModelIndex(), size, size + list.size() - 1);
for (const Shared::Message& msg : list) {
Shared::Message* copy = new Shared::Message(msg);
storage.insert(copy);
@@ -143,3 +171,7 @@ void MessageFeed::responseArchive(const std::list list)
endInsertRows();
}
+QHash MessageFeed::roleNames() const
+{
+ return roles;
+}
diff --git a/ui/models/messagefeed.h b/ui/models/messagefeed.h
index 7ca72f9..1aeb476 100644
--- a/ui/models/messagefeed.h
+++ b/ui/models/messagefeed.h
@@ -47,6 +47,8 @@ public:
bool canFetchMore(const QModelIndex & parent) const override;
void fetchMore(const QModelIndex & parent) override;
+ QHash roleNames() const override;
+
void responseArchive(const std::list list);
unsigned int unreadMessagesCount() const;
@@ -54,6 +56,15 @@ public:
signals:
void requestArchive(const QString& before);
+public:
+ enum MessageRoles {
+ Text = Qt::UserRole + 1,
+ Sender,
+ Date,
+ DeliveryState,
+ Correction,
+ SentByMe
+ };
private:
enum SyncState {
incomplete,
@@ -81,7 +92,8 @@ private:
Shared::Message,
QDateTime,
&Shared::Message::getTime
- >
+ >,
+ std::greater
>
>
> Storage;
@@ -94,6 +106,7 @@ private:
SyncState syncState;
+ static const QHash roles;
};
diff --git a/ui/widgets/CMakeLists.txt b/ui/widgets/CMakeLists.txt
index 29e2dbc..0932100 100644
--- a/ui/widgets/CMakeLists.txt
+++ b/ui/widgets/CMakeLists.txt
@@ -7,7 +7,7 @@ set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
# Find the QtWidgets library
-find_package(Qt5Widgets CONFIG REQUIRED)
+find_package(Qt5Widgets CONFIG REQUIRED COMPONENTS Widgets Quick Qml QuickControls2 Core)
add_subdirectory(vcard)
@@ -21,9 +21,12 @@ set(squawkWidgets_SRC
joinconference.cpp
)
-# Tell CMake to create the helloworld executable
add_library(squawkWidgets ${squawkWidgets_SRC})
# Use the Widgets module from Qt 5.
target_link_libraries(squawkWidgets vCardUI)
target_link_libraries(squawkWidgets Qt5::Widgets)
+target_link_libraries(squawkWidgets Qt5::Qml)
+target_link_libraries(squawkWidgets Qt5::QuickControls2)
+
+qt5_use_modules(squawkWidgets Quick Qml QuickControls2 Core Widgets)
diff --git a/ui/widgets/chat.cpp b/ui/widgets/chat.cpp
index 379b01e..1b20e86 100644
--- a/ui/widgets/chat.cpp
+++ b/ui/widgets/chat.cpp
@@ -19,7 +19,7 @@
#include "chat.h"
Chat::Chat(Models::Account* acc, Models::Contact* p_contact, QWidget* parent):
- Conversation(false, acc, p_contact->getJid(), "", parent),
+ Conversation(false, acc, p_contact, p_contact->getJid(), "", parent),
contact(p_contact)
{
setName(p_contact->getContactName());
@@ -28,8 +28,6 @@ Chat::Chat(Models::Account* acc, Models::Contact* p_contact, QWidget* parent):
setAvatar(p_contact->getAvatarPath());
connect(contact, &Models::Contact::childChanged, this, &Chat::onContactChanged);
-
- feed->setModel(p_contact->feed);
}
Chat::~Chat()
diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp
index fced226..6643865 100644
--- a/ui/widgets/conversation.cpp
+++ b/ui/widgets/conversation.cpp
@@ -29,7 +29,7 @@
#include
#include
-Conversation::Conversation(bool muc, Models::Account* acc, const QString pJid, const QString pRes, QWidget* parent):
+Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, const QString pJid, const QString pRes, QWidget* parent):
QWidget(parent),
isMuc(muc),
account(acc),
@@ -45,7 +45,7 @@ Conversation::Conversation(bool muc, Models::Account* acc, const QString pJid, c
filesLayout(0),
overlay(new QWidget()),
filesToAttach(),
- feed(0),
+ feed(new QQuickView()),
scroll(down),
manualSliderChange(false),
requestingHistory(false),
@@ -53,7 +53,14 @@ Conversation::Conversation(bool muc, Models::Account* acc, const QString pJid, c
tsb(QApplication::style()->styleHint(QStyle::SH_ScrollBar_Transient) == 1)
{
m_ui->setupUi(this);
- feed = m_ui->feed;
+
+ feed->setColor(QWidget::palette().color(QPalette::Base));
+ feed->setInitialProperties({{"model", QVariant::fromValue(el->feed)}});
+ feed->setResizeMode(QQuickView::SizeRootObjectToView);
+ feed->setSource(QUrl("qrc:/qml/feed.qml"));
+ QWidget *container = QWidget::createWindowContainer(feed, this);
+ container->setAutoFillBackground(false);
+ m_ui->widget->layout()->addWidget(container);
connect(acc, &Models::Account::childChanged, this, &Conversation::onAccountChanged);
diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h
index f870e76..b0e1b79 100644
--- a/ui/widgets/conversation.h
+++ b/ui/widgets/conversation.h
@@ -24,7 +24,7 @@
#include
#include
#include
-#include
+#include
#include "shared/message.h"
#include "order.h"
@@ -72,7 +72,7 @@ class Conversation : public QWidget
{
Q_OBJECT
public:
- Conversation(bool muc, Models::Account* acc, const QString pJid, const QString pRes, QWidget* parent = 0);
+ Conversation(bool muc, Models::Account* acc, Models::Element* el, const QString pJid, const QString pRes, QWidget* parent = 0);
~Conversation();
QString getJid() const;
@@ -133,6 +133,7 @@ protected:
down
};
Models::Account* account;
+ Models::Element* element;
QString palJid;
QString activePalResource;
QScopedPointer m_ui;
@@ -145,7 +146,7 @@ protected:
FlowLayout* filesLayout;
QWidget* overlay;
W::Order filesToAttach;
- QListView* feed;
+ QQuickView* feed;
Scroll scroll;
bool manualSliderChange;
bool requestingHistory;
diff --git a/ui/widgets/conversation.ui b/ui/widgets/conversation.ui
index 898f0ff..bb38666 100644
--- a/ui/widgets/conversation.ui
+++ b/ui/widgets/conversation.ui
@@ -214,37 +214,6 @@
- -
-
-
- QFrame::NoFrame
-
-
- Qt::ScrollBarAlwaysOff
-
-
- QAbstractItemView::NoEditTriggers
-
-
- false
-
-
- QAbstractItemView::NoSelection
-
-
- QAbstractItemView::ScrollPerPixel
-
-
- QAbstractItemView::ScrollPerItem
-
-
- false
-
-
- true
-
-
-
diff --git a/ui/widgets/room.cpp b/ui/widgets/room.cpp
index 8e3b813..91d7255 100644
--- a/ui/widgets/room.cpp
+++ b/ui/widgets/room.cpp
@@ -19,7 +19,7 @@
#include "room.h"
Room::Room(Models::Account* acc, Models::Room* p_room, QWidget* parent):
- Conversation(true, acc, p_room->getJid(), "", parent),
+ Conversation(true, acc, p_room, p_room->getJid(), "", parent),
room(p_room)
{
setName(p_room->getName());
@@ -29,8 +29,6 @@ Room::Room(Models::Account* acc, Models::Room* p_room, QWidget* parent):
connect(room, &Models::Room::childChanged, this, &Room::onRoomChanged);
connect(room, &Models::Room::participantJoined, this, &Room::onParticipantJoined);
connect(room, &Models::Room::participantLeft, this, &Room::onParticipantLeft);
-
- feed->setModel(p_room->feed);
}
Room::~Room()