From bdf4ee43c84a6ba519cdc8e4fb65d7aaf90f4e19 Mon Sep 17 00:00:00 2001 From: Carl Schwan Date: Thu, 22 Feb 2024 00:13:45 +0100 Subject: [PATCH] Use TreeItem from qt tree model example in RoomTreeModel --- src/models/roomtreemodel.cpp | 335 +++++++++++++++++++++-------------- src/models/roomtreemodel.h | 29 ++- 2 files changed, 233 insertions(+), 131 deletions(-) diff --git a/src/models/roomtreemodel.cpp b/src/models/roomtreemodel.cpp index f88e244d5..d2b8069c9 100644 --- a/src/models/roomtreemodel.cpp +++ b/src/models/roomtreemodel.cpp @@ -13,6 +13,152 @@ using namespace Quotient; +TreeItem::TreeItem(TreeData treeData, TreeItem *parent) + : m_treeData(treeData) + , m_parentItem(parent) +{ +} + +void TreeItem::appendChild(std::unique_ptr &&child) +{ + m_childItems.push_back(std::move(child)); +} + +bool TreeItem::insertChildren(int position, int count, TreeData treeData) +{ + if (position < 0 || position > qsizetype(m_childItems.size())) + return false; + + for (int row = 0; row < count; ++row) { + m_childItems.insert(m_childItems.cbegin() + position, std::make_unique(treeData, this)); + } + + return true; +} + +bool TreeItem::removeChildren(int position, int count) +{ + if (position < 0 || position + count > qsizetype(m_childItems.size())) { + return false; + } + + for (int row = 0; row < count; ++row) { + m_childItems.erase(m_childItems.cbegin() + position); + } + + return true; +} + +TreeItem *TreeItem::child(int row) +{ + return row >= 0 && row < childCount() ? m_childItems.at(row).get() : nullptr; +} + +int TreeItem::childCount() const +{ + return int(m_childItems.size()); +} + +int TreeItem::row() const +{ + if (m_parentItem == nullptr) { + return 0; + } + const auto it = std::find_if(m_parentItem->m_childItems.cbegin(), m_parentItem->m_childItems.cend(), [this](const std::unique_ptr &treeItem) { + return treeItem.get() == this; + }); + + if (it != m_parentItem->m_childItems.cend()) + return std::distance(m_parentItem->m_childItems.cbegin(), it); + Q_ASSERT(false); // should not happen + return -1; +} + +QVariant TreeItem::data(int role) const +{ + if (!m_parentItem) { + return {}; + } + + if (std::holds_alternative(m_treeData)) { + const auto row = this->row(); + switch (role) { + case RoomTreeModel::DisplayNameRole: + return NeoChatRoomType::typeName(row); + case RoomTreeModel::DelegateTypeRole: + if (row == NeoChatRoomType::Search) { + return QStringLiteral("search"); + } + if (row == NeoChatRoomType::AddDirect) { + return QStringLiteral("addDirect"); + } + return QStringLiteral("section"); + case RoomTreeModel::IconRole: + return NeoChatRoomType::typeIconName(row); + case RoomTreeModel::CategoryRole: + return row; + default: + return {}; + } + } + + const auto room = std::get(m_treeData); + + switch (role) { + case RoomTreeModel::DisplayNameRole: + return room->displayName(); + case RoomTreeModel::AvatarRole: + return room->avatarMediaId(); + case RoomTreeModel::CanonicalAliasRole: + return room->canonicalAlias(); + case RoomTreeModel::TopicRole: + return room->topic(); + case RoomTreeModel::CategoryRole: + return NeoChatRoomType::typeForRoom(room); + case RoomTreeModel::NotificationCountRole: + return room->notificationCount(); + case RoomTreeModel::HighlightCountRole: + return room->highlightCount(); + case RoomTreeModel::LastActiveTimeRole: + return room->lastActiveTime(); + case RoomTreeModel::JoinStateRole: + if (!room->successorId().isEmpty()) { + return QStringLiteral("upgraded"); + } + return QVariant::fromValue(room->joinState()); + case RoomTreeModel::CurrentRoomRole: + return QVariant::fromValue(room); + case RoomTreeModel::SubtitleTextRole: { + if (room->lastEvent() == nullptr || room->lastEventIsSpoiler()) { + return QString(); + } + EventHandler eventHandler(room, room->lastEvent()); + return eventHandler.subtitleText(); + } + case RoomTreeModel::AvatarImageRole: + return room->avatar(128); + case RoomTreeModel::RoomIdRole: + return room->id(); + case RoomTreeModel::IsSpaceRole: + return room->isSpace(); + case RoomTreeModel::IsChildSpaceRole: + return SpaceHierarchyCache::instance().isChild(room->id()); + case RoomTreeModel::ReplacementIdRole: + return room->successorId(); + case RoomTreeModel::IsDirectChat: + return room->isDirectChat(); + case RoomTreeModel::DelegateTypeRole: + return QStringLiteral("normal"); + } + + return {}; +} + +TreeItem *TreeItem::parentItem() const +{ + return m_parentItem; +} + RoomTreeModel::RoomTreeModel(QObject *parent) : QAbstractItemModel(parent) { @@ -27,11 +173,22 @@ void RoomTreeModel::initializeCategories() } } m_rooms.clear(); + + m_rootItem.reset(new TreeItem(nullptr)); for (int i = 0; i < 8; i++) { - m_rooms[NeoChatRoomType::Types(i)] = {}; + m_rootItem->appendChild(std::make_unique(NeoChatRoomType::Types(i), m_rootItem.get())); } } +TreeItem *RoomTreeModel::getItem(const QModelIndex &index) const +{ + if (index.isValid()) { + if (auto *item = static_cast(index.internalPointer())) + return item; + } + return m_rootItem.get(); +} + void RoomTreeModel::setConnection(NeoChatConnection *connection) { if (m_connection == connection) { @@ -77,15 +234,21 @@ void RoomTreeModel::newRoom(Room *r) void RoomTreeModel::leftRoom(Room *r) { const auto room = dynamic_cast(r); - const auto type = NeoChatRoomType::typeForRoom(room); - auto row = m_rooms[type].indexOf(room); - if (row == -1) { + + auto idx = indexForRoom(room); + if (!idx.isValid()) { return; } - beginRemoveRows(index(type, 0), row, row); + + beginRemoveRows(idx.parent(), idx.row(), idx.row()); + const bool success = parentItem->removeChildren(position, rows); m_rooms[type][row]->disconnect(this); m_rooms[type].removeAt(row); endRemoveRows(); + + if (success) { + qWarning() << "Unable to remove room"; + } } void RoomTreeModel::moveRoom(Quotient::Room *room) @@ -151,14 +314,12 @@ void RoomTreeModel::connectRoomSignals(NeoChatRoom *room) void RoomTreeModel::refreshRoomRoles(NeoChatRoom *room, const QList &roles) { - const auto roomType = NeoChatRoomType::typeForRoom(room); - const auto it = std::find(m_rooms[roomType].begin(), m_rooms[roomType].end(), room); - if (it == m_rooms[roomType].end()) { + const auto idx = indexForRoom(room); + if (!idx.isValid()) { qCritical() << "Room" << room->id() << "not found in the room list"; return; } - const auto parentIndex = index(roomType, 0, {}); - const auto idx = index(it - m_rooms[roomType].begin(), 0, parentIndex); + Q_EMIT dataChanged(idx, idx, roles); } @@ -175,32 +336,40 @@ int RoomTreeModel::columnCount(const QModelIndex &parent) const int RoomTreeModel::rowCount(const QModelIndex &parent) const { - if (!parent.isValid()) { - return m_rooms.keys().size(); + if (parent.isValid() && parent.column() > 0) { + return 0; } - if (!parent.parent().isValid()) { - return m_rooms.values()[parent.row()].size(); - } - return 0; + + const TreeItem *parentItem = getItem(parent); + return parentItem ? parentItem->childCount() : 0; } QModelIndex RoomTreeModel::parent(const QModelIndex &index) const { - if (!index.internalPointer()) { + if (!index.isValid()) return {}; - } - return this->index(NeoChatRoomType::typeForRoom(static_cast(index.internalPointer())), 0, QModelIndex()); + + TreeItem *childItem = getItem(index); + TreeItem *parentItem = childItem ? childItem->parentItem() : nullptr; + + return (parentItem != m_rootItem.get() && parentItem != nullptr) ? createIndex(parentItem->row(), 0, parentItem) : QModelIndex{}; } QModelIndex RoomTreeModel::index(int row, int column, const QModelIndex &parent) const { - if (!parent.isValid()) { - return createIndex(row, column, nullptr); - } - if (row >= rowCount(parent)) { + if (parent.isValid() && parent.column() != 0) { return {}; } - return createIndex(row, column, m_rooms[NeoChatRoomType::Types(parent.row())][row]); + + TreeItem *parentItem = getItem(parent); + if (!parentItem) { + return {}; + } + + if (auto *childItem = parentItem->child(row)) { + return createIndex(row, column, childItem); + } + return {}; } QHash RoomTreeModel::roleNames() const @@ -231,103 +400,9 @@ QHash RoomTreeModel::roleNames() const // TODO room type changes QVariant RoomTreeModel::data(const QModelIndex &index, int role) const { - if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid)) { - return QVariant(); - } + Q_ASSERT(checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid)); - if (!index.parent().isValid()) { - if (role == DisplayNameRole) { - return NeoChatRoomType::typeName(index.row()); - } - if (role == DelegateTypeRole) { - if (index.row() == NeoChatRoomType::Search) { - return QStringLiteral("search"); - } - if (index.row() == NeoChatRoomType::AddDirect) { - return QStringLiteral("addDirect"); - } - return QStringLiteral("section"); - } - if (role == IconRole) { - return NeoChatRoomType::typeIconName(index.row()); - } - if (role == CategoryRole) { - return index.row(); - } - return {}; - } - const auto room = m_rooms.values()[index.parent().row()][index.row()].get(); - Q_ASSERT(room); - - if (role == DisplayNameRole) { - return room->displayName(); - } - if (role == AvatarRole) { - return room->avatarMediaId(); - } - if (role == CanonicalAliasRole) { - return room->canonicalAlias(); - } - if (role == TopicRole) { - return room->topic(); - } - if (role == CategoryRole) { - return NeoChatRoomType::typeForRoom(room); - } - if (role == ContextNotificationCountRole) { - return int(room->contextAwareNotificationCount()); - } - if (role == HasHighlightNotificationsRole) { - return room->highlightCount() > 0 && room->contextAwareNotificationCount() > 0; - } - if (role == LastActiveTimeRole) { - return room->lastActiveTime(); - } - if (role == JoinStateRole) { - if (!room->successorId().isEmpty()) { - return QStringLiteral("upgraded"); - } - return QVariant::fromValue(room->joinState()); - } - if (role == CurrentRoomRole) { - return QVariant::fromValue(room); - } - if (role == SubtitleTextRole) { - if (room->lastEvent() == nullptr || room->lastEventIsSpoiler()) { - return QString(); - } - EventHandler eventHandler(room, room->lastEvent()); - return eventHandler.subtitleText(); - } - if (role == AvatarImageRole) { - return room->avatar(128); - } - if (role == RoomIdRole) { - return room->id(); - } - if (role == IsSpaceRole) { - return room->isSpace(); - } - if (role == IsChildSpaceRole) { - return SpaceHierarchyCache::instance().isChild(room->id()); - } - if (role == ReplacementIdRole) { - return room->successorId(); - } - if (role == IsDirectChat) { - return room->isDirectChat(); - } - if (role == DelegateTypeRole) { - return QStringLiteral("normal"); - } - if (role == AttentionRole) { - return room->notificationCount() + room->highlightCount() > 0; - } - if (role == FavouriteRole) { - return room->isFavourite(); - } - - return {}; + return getItem(index)->data(role); } QModelIndex RoomTreeModel::indexForRoom(NeoChatRoom *room) const @@ -336,18 +411,18 @@ QModelIndex RoomTreeModel::indexForRoom(NeoChatRoom *room) const return {}; } - // Try and find by checking type. - const auto type = NeoChatRoomType::typeForRoom(room); - auto row = m_rooms[type].indexOf(room); - if (row >= 0) { - return index(row, 0, index(type, 0)); - } - // Double check that the room isn't in the wrong category. - for (const auto &key : m_rooms.keys()) { - if (m_rooms[key].contains(room)) { - return index(m_rooms[key].indexOf(room), 0, index(key, 0)); + const auto roomType = NeoChatRoomType::typeForRoom(room); + const auto roomTypeItem = m_rootItem->child(roomType); + + for (int i = 0, count = roomTypeItem->childCount(); i < count; i++) { + auto roomItem = roomTypeItem->child(i); + if (std::get(roomItem->treeData()) == room) { + const auto parentIndex = index(roomType, 0, {}); + const auto idx = index(i, 0, parentIndex); + return idx; } } + return {}; } diff --git a/src/models/roomtreemodel.h b/src/models/roomtreemodel.h index 4c3bae33a..5334bb437 100644 --- a/src/models/roomtreemodel.h +++ b/src/models/roomtreemodel.h @@ -16,6 +16,30 @@ class Room; class NeoChatConnection; class NeoChatRoom; +class TreeItem +{ +public: + using TreeData = std::variant; + + explicit TreeItem(TreeData data, TreeItem *parentItem = nullptr); + + TreeItem *child(int row); + int childCount() const; + QVariant data(int role) const; + void appendChild(std::unique_ptr &&child); + bool insertChildren(int position, int count, TreeData treeData); + TreeItem *parentItem() const; + bool removeChildren(int position, int count); + bool removeColumns(int position, int columns); + int row() const; + TreeData treeData() const; + +private: + std::vector> m_childItems; + TreeData m_treeData; + TreeItem *m_parentItem; +}; + class RoomTreeModel : public QAbstractItemModel { Q_OBJECT @@ -76,6 +100,8 @@ public: int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; + TreeItem *getItem(const QModelIndex &index) const; + Q_INVOKABLE QModelIndex indexForRoom(NeoChatRoom *room) const; Q_SIGNALS: @@ -83,7 +109,6 @@ Q_SIGNALS: private: QPointer m_connection = nullptr; - QMap>> m_rooms; void initializeCategories(); void connectRoomSignals(NeoChatRoom *room); @@ -93,4 +118,6 @@ private: void moveRoom(Quotient::Room *room); void refreshRoomRoles(NeoChatRoom *room, const QList &roles = {}); + + std::unique_ptr m_rootItem; };