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()