Use TreeItem from qt tree model example in RoomTreeModel

This commit is contained in:
Carl Schwan
2024-02-22 00:13:45 +01:00
parent c6300179d8
commit bdf4ee43c8
2 changed files with 233 additions and 131 deletions

View File

@@ -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<TreeItem> &&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<TreeItem>(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> &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<NeoChatRoomType::Types>(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<NeoChatRoom *>(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<TreeItem>(NeoChatRoomType::Types(i), m_rootItem.get()));
}
}
TreeItem *RoomTreeModel::getItem(const QModelIndex &index) const
{
if (index.isValid()) {
if (auto *item = static_cast<TreeItem *>(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<NeoChatRoom *>(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<int> &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<NeoChatRoom *>(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<int, QByteArray> RoomTreeModel::roleNames() const
@@ -231,103 +400,9 @@ QHash<int, QByteArray> 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<NeoChatRoom *>(roomItem->treeData()) == room) {
const auto parentIndex = index(roomType, 0, {});
const auto idx = index(i, 0, parentIndex);
return idx;
}
}
return {};
}

View File

@@ -16,6 +16,30 @@ class Room;
class NeoChatConnection;
class NeoChatRoom;
class TreeItem
{
public:
using TreeData = std::variant<NeoChatRoom *, NeoChatRoomType::Types>;
explicit TreeItem(TreeData data, TreeItem *parentItem = nullptr);
TreeItem *child(int row);
int childCount() const;
QVariant data(int role) const;
void appendChild(std::unique_ptr<TreeItem> &&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<std::unique_ptr<TreeItem>> 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<NeoChatConnection> m_connection = nullptr;
QMap<NeoChatRoomType::Types, QList<QPointer<NeoChatRoom>>> m_rooms;
void initializeCategories();
void connectRoomSignals(NeoChatRoom *room);
@@ -93,4 +118,6 @@ private:
void moveRoom(Quotient::Room *room);
void refreshRoomRoles(NeoChatRoom *room, const QList<int> &roles = {});
std::unique_ptr<TreeItem> m_rootItem;
};