Use TreeItem from qt tree model example in RoomTreeModel
This commit is contained in:
@@ -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 {};
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user