Skip to content
Snippets Groups Projects
Verified Commit fe49beb6 authored by Nicolas Werner's avatar Nicolas Werner
Browse files

Hide me underneath the space tree

parent b505fa42
No related branches found
No related tags found
No related merge requests found
Pipeline #2207 passed
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.47 4.22a.75.75 0 0 0 0 1.06L15.19 12l-6.72 6.72a.75.75 0 1 0 1.06 1.06l7.25-7.25a.75.75 0 0 0 0-1.06L9.53 4.22a.75.75 0 0 0-1.06 0Z" fill="#212121"/></svg>
\ No newline at end of file
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M4.22 8.47a.75.75 0 0 1 1.06 0L12 15.19l6.72-6.72a.75.75 0 1 1 1.06 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L4.22 9.53a.75.75 0 0 1 0-1.06Z" fill="#212121"/></svg>
\ No newline at end of file
...@@ -11,6 +11,7 @@ import QtQuick.Layouts 1.3 ...@@ -11,6 +11,7 @@ import QtQuick.Layouts 1.3
import im.nheko 1.0 import im.nheko 1.0
Page { Page {
id: communitySidebar
//leftPadding: Nheko.paddingSmall //leftPadding: Nheko.paddingSmall
//rightPadding: Nheko.paddingSmall //rightPadding: Nheko.paddingSmall
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 1.6) property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 1.6)
...@@ -22,7 +23,7 @@ Page { ...@@ -22,7 +23,7 @@ Page {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
height: parent.height height: parent.height
model: Communities model: Communities.filtered()
ScrollHelper { ScrollHelper {
flickable: parent flickable: parent
...@@ -107,9 +108,31 @@ Page { ...@@ -107,9 +108,31 @@ Page {
} }
RowLayout { RowLayout {
id: r
spacing: Nheko.paddingMedium spacing: Nheko.paddingMedium
anchors.fill: parent anchors.fill: parent
anchors.margins: Nheko.paddingMedium anchors.margins: Nheko.paddingMedium
anchors.leftMargin: communitySidebar.collapsed ? Nheko.paddingMedium : (Nheko.paddingMedium * (model.depth + 1))
ImageButton {
visible: !communitySidebar.collapsed && model.collapsible
Layout.preferredHeight: fontMetrics.lineSpacing
Layout.preferredWidth: fontMetrics.lineSpacing
Layout.alignment: Qt.AlignVCenter
height: fontMetrics.lineSpacing
width: fontMetrics.lineSpacing
image: model.collapsed ? ":/icons/icons/ui/collapsed.svg" : ":/icons/icons/ui/expanded.svg"
ToolTip.visible: hovered
ToolTip.text: model.collapsed ? qsTr("Expand") : qsTr("Collapse")
hoverEnabled: true
onClicked: model.collapsed = !model.collapsed
}
Item {
Layout.preferredWidth: fontMetrics.lineSpacing
visible: !communitySidebar.collapsed && !model.collapsible
}
Avatar { Avatar {
id: avatar id: avatar
...@@ -130,10 +153,10 @@ Page { ...@@ -130,10 +153,10 @@ Page {
} }
ElidedLabel { ElidedLabel {
visible: !collapsed visible: !communitySidebar.collapsed
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
color: communityItem.importantText color: communityItem.importantText
elideWidth: parent.width - avatar.width - Nheko.paddingMedium elideWidth: parent.width - avatar.width - r.anchors.leftMargin - Nheko.paddingMedium - fontMetrics.lineSpacing
fullText: model.displayName fullText: model.displayName
textFormat: Text.PlainText textFormat: Text.PlainText
} }
......
<RCC> <RCC>
<qresource prefix="/icons"> <qresource prefix="/icons">
<file>icons/ui/sticky-note-solid.svg</file>
<file>icons/ui/add-square-button.svg</file> <file>icons/ui/add-square-button.svg</file>
<file>icons/ui/send.svg</file>
<file>icons/ui/smile.svg</file>
<file>icons/ui/user-friends-solid.svg</file>
<file>icons/ui/place-call.svg</file>
<file>icons/ui/attach.svg</file>
<file>icons/ui/angle-arrow-left.svg</file> <file>icons/ui/angle-arrow-left.svg</file>
<file>icons/ui/attach.svg</file>
<file>icons/ui/ban.svg</file>
<file>icons/ui/chat.svg</file> <file>icons/ui/chat.svg</file>
<file>icons/ui/checkmark.svg</file> <file>icons/ui/checkmark.svg</file>
<file>icons/ui/clock.svg</file> <file>icons/ui/clock.svg</file>
<file>icons/ui/collapsed.svg</file>
<file>icons/ui/delete.svg</file> <file>icons/ui/delete.svg</file>
<file>icons/ui/dismiss.svg</file>
<file>icons/ui/double-checkmark.svg</file>
<file>icons/ui/download.svg</file>
<file>icons/ui/edit.svg</file> <file>icons/ui/edit.svg</file>
<file>icons/ui/end-call.svg</file> <file>icons/ui/end-call.svg</file>
<file>icons/ui/expanded.svg</file>
<file>icons/ui/image-failed.svg</file>
<file>icons/ui/lowprio.svg</file> <file>icons/ui/lowprio.svg</file>
<file>icons/ui/microphone-mute.svg</file> <file>icons/ui/microphone-mute.svg</file>
<file>icons/ui/microphone-unmute.svg</file> <file>icons/ui/microphone-unmute.svg</file>
<file>icons/ui/options.svg</file>
<file>icons/ui/pause-symbol.svg</file> <file>icons/ui/pause-symbol.svg</file>
<file>icons/ui/people.svg</file> <file>icons/ui/people.svg</file>
<file>icons/ui/picture-in-picture.svg</file>
<file>icons/ui/place-call.svg</file>
<file>icons/ui/play-sign.svg</file> <file>icons/ui/play-sign.svg</file>
<file>icons/ui/power-off.svg</file> <file>icons/ui/power-off.svg</file>
<file>icons/ui/refresh.svg</file> <file>icons/ui/refresh.svg</file>
...@@ -26,21 +31,18 @@ ...@@ -26,21 +31,18 @@
<file>icons/ui/round-remove-button.svg</file> <file>icons/ui/round-remove-button.svg</file>
<file>icons/ui/screen-share.svg</file> <file>icons/ui/screen-share.svg</file>
<file>icons/ui/search.svg</file> <file>icons/ui/search.svg</file>
<file>icons/ui/send.svg</file>
<file>icons/ui/settings.svg</file> <file>icons/ui/settings.svg</file>
<file>icons/ui/smile.svg</file>
<file>icons/ui/speech-bubbles.svg</file> <file>icons/ui/speech-bubbles.svg</file>
<file>icons/ui/star.svg</file> <file>icons/ui/star.svg</file>
<file>icons/ui/sticky-note-solid.svg</file>
<file>icons/ui/tag.svg</file> <file>icons/ui/tag.svg</file>
<file>icons/ui/user-friends-solid.svg</file>
<file>icons/ui/video.svg</file> <file>icons/ui/video.svg</file>
<file>icons/ui/volume-off-indicator.svg</file> <file>icons/ui/volume-off-indicator.svg</file>
<file>icons/ui/volume-up.svg</file> <file>icons/ui/volume-up.svg</file>
<file>icons/ui/world.svg</file> <file>icons/ui/world.svg</file>
<file>icons/ui/picture-in-picture.svg</file>
<file>icons/ui/options.svg</file>
<file>icons/ui/double-checkmark.svg</file>
<file>icons/ui/ban.svg</file>
<file>icons/ui/image-failed.svg</file>
<file>icons/ui/dismiss.svg</file>
<file>icons/ui/download.svg</file>
<file>icons/emoji-categories/activity.svg</file> <file>icons/emoji-categories/activity.svg</file>
<file>icons/emoji-categories/flags.svg</file> <file>icons/emoji-categories/flags.svg</file>
<file>icons/emoji-categories/foods.svg</file> <file>icons/emoji-categories/foods.svg</file>
......
...@@ -95,6 +95,12 @@ public: ...@@ -95,6 +95,12 @@ public:
auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY);
return getStateEvent<T>(txn, room_id, state_key); return getStateEvent<T>(txn, room_id, state_key);
} }
template<typename T>
std::vector<mtx::events::StateEvent<T>> getStateEventsWithType(const std::string &room_id)
{
auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY);
return getStateEventsWithType<T>(txn, room_id);
}
//! retrieve a specific event from account data //! retrieve a specific event from account data
//! pass empty room_id for global account data //! pass empty room_id for global account data
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
#include <set> #include <set>
#include "Cache.h" #include "Cache.h"
#include "Cache_p.h"
#include "Logging.h"
#include "UserSettingsPage.h" #include "UserSettingsPage.h"
CommunitiesModel::CommunitiesModel(QObject *parent) CommunitiesModel::CommunitiesModel(QObject *parent)
...@@ -20,12 +22,29 @@ CommunitiesModel::roleNames() const ...@@ -20,12 +22,29 @@ CommunitiesModel::roleNames() const
{AvatarUrl, "avatarUrl"}, {AvatarUrl, "avatarUrl"},
{DisplayName, "displayName"}, {DisplayName, "displayName"},
{Tooltip, "tooltip"}, {Tooltip, "tooltip"},
{ChildrenHidden, "childrenHidden"}, {Collapsed, "collapsed"},
{Collapsible, "collapsible"},
{Hidden, "hidden"}, {Hidden, "hidden"},
{Depth, "depth"},
{Id, "id"}, {Id, "id"},
}; };
} }
bool
CommunitiesModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (role != CommunitiesModel::Collapsed)
return false;
else if (index.row() >= 2 || index.row() - 2 < spaceOrder_.size()) {
spaceOrder_.tree.at(index.row() - 2).collapsed = value.toBool();
const auto cindex = spaceOrder_.lastChild(index.row() - 2);
emit dataChanged(index, this->index(cindex + 2), {Collapsed, Qt::DisplayRole});
return true;
} else
return false;
}
QVariant QVariant
CommunitiesModel::data(const QModelIndex &index, int role) const CommunitiesModel::data(const QModelIndex &index, int role) const
{ {
...@@ -37,10 +56,16 @@ CommunitiesModel::data(const QModelIndex &index, int role) const ...@@ -37,10 +56,16 @@ CommunitiesModel::data(const QModelIndex &index, int role) const
return tr("All rooms"); return tr("All rooms");
case CommunitiesModel::Roles::Tooltip: case CommunitiesModel::Roles::Tooltip:
return tr("Shows all rooms without filtering."); return tr("Shows all rooms without filtering.");
case CommunitiesModel::Roles::ChildrenHidden: case CommunitiesModel::Roles::Collapsed:
return false;
case CommunitiesModel::Roles::Collapsible:
return false; return false;
case CommunitiesModel::Roles::Hidden: case CommunitiesModel::Roles::Hidden:
return false; return false;
case CommunitiesModel::Roles::Parent:
return "";
case CommunitiesModel::Roles::Depth:
return 0;
case CommunitiesModel::Roles::Id: case CommunitiesModel::Roles::Id:
return ""; return "";
} }
...@@ -52,25 +77,43 @@ CommunitiesModel::data(const QModelIndex &index, int role) const ...@@ -52,25 +77,43 @@ CommunitiesModel::data(const QModelIndex &index, int role) const
return tr("Direct Chats"); return tr("Direct Chats");
case CommunitiesModel::Roles::Tooltip: case CommunitiesModel::Roles::Tooltip:
return tr("Show direct chats."); return tr("Show direct chats.");
case CommunitiesModel::Roles::ChildrenHidden: case CommunitiesModel::Roles::Collapsed:
return false;
case CommunitiesModel::Roles::Collapsible:
return false; return false;
case CommunitiesModel::Roles::Hidden: case CommunitiesModel::Roles::Hidden:
return hiddentTagIds_.contains("dm"); return hiddentTagIds_.contains("dm");
case CommunitiesModel::Roles::Parent:
return "";
case CommunitiesModel::Roles::Depth:
return 0;
case CommunitiesModel::Roles::Id: case CommunitiesModel::Roles::Id:
return "dm"; return "dm";
} }
} else if (index.row() - 2 < spaceOrder_.size()) { } else if (index.row() - 2 < spaceOrder_.size()) {
auto id = spaceOrder_.at(index.row() - 2); auto id = spaceOrder_.tree.at(index.row() - 2).name;
switch (role) { switch (role) {
case CommunitiesModel::Roles::AvatarUrl: case CommunitiesModel::Roles::AvatarUrl:
return QString::fromStdString(spaces_.at(id).avatar_url); return QString::fromStdString(spaces_.at(id).avatar_url);
case CommunitiesModel::Roles::DisplayName: case CommunitiesModel::Roles::DisplayName:
case CommunitiesModel::Roles::Tooltip: case CommunitiesModel::Roles::Tooltip:
return QString::fromStdString(spaces_.at(id).name); return QString::fromStdString(spaces_.at(id).name);
case CommunitiesModel::Roles::ChildrenHidden: case CommunitiesModel::Roles::Collapsed:
return true; return spaceOrder_.tree.at(index.row() - 2).collapsed;
case CommunitiesModel::Roles::Collapsible: {
auto idx = index.row() - 2;
return idx != spaceOrder_.lastChild(idx);
}
case CommunitiesModel::Roles::Hidden: case CommunitiesModel::Roles::Hidden:
return hiddentTagIds_.contains("space:" + id); return hiddentTagIds_.contains("space:" + id);
case CommunitiesModel::Roles::Parent: {
if (auto p = spaceOrder_.parent(index.row() - 2); p >= 0)
return spaceOrder_.tree[p].name;
return "";
}
case CommunitiesModel::Roles::Depth:
return spaceOrder_.tree.at(index.row() - 2).depth;
case CommunitiesModel::Roles::Id: case CommunitiesModel::Roles::Id:
return "space:" + id; return "space:" + id;
} }
...@@ -116,8 +159,14 @@ CommunitiesModel::data(const QModelIndex &index, int role) const ...@@ -116,8 +159,14 @@ CommunitiesModel::data(const QModelIndex &index, int role) const
switch (role) { switch (role) {
case CommunitiesModel::Roles::Hidden: case CommunitiesModel::Roles::Hidden:
return hiddentTagIds_.contains("tag:" + tag); return hiddentTagIds_.contains("tag:" + tag);
case CommunitiesModel::Roles::ChildrenHidden: case CommunitiesModel::Roles::Collapsed:
return true; return true;
case CommunitiesModel::Roles::Collapsible:
return false;
case CommunitiesModel::Roles::Parent:
return "";
case CommunitiesModel::Roles::Depth:
return 0;
case CommunitiesModel::Roles::Id: case CommunitiesModel::Roles::Id:
return "tag:" + tag; return "tag:" + tag;
} }
...@@ -125,21 +174,67 @@ CommunitiesModel::data(const QModelIndex &index, int role) const ...@@ -125,21 +174,67 @@ CommunitiesModel::data(const QModelIndex &index, int role) const
return QVariant(); return QVariant();
} }
namespace {
struct temptree
{
std::map<std::string, temptree> children;
void insert(const std::vector<std::string> &parents, const std::string &child)
{
temptree *t = this;
for (const auto &e : parents)
t = &t->children[e];
t->children[child];
}
void flatten(CommunitiesModel::FlatTree &to, int i = 0) const
{
for (const auto &[child, subtree] : children) {
to.tree.push_back({QString::fromStdString(child), i, false});
subtree.flatten(to, i + 1);
}
}
};
void
addChildren(temptree &t,
std::vector<std::string> path,
std::string child,
const std::map<std::string, std::set<std::string>> &children)
{
if (std::find(path.begin(), path.end(), child) != path.end())
return;
path.push_back(child);
if (children.count(child)) {
for (const auto &c : children.at(child)) {
t.insert(path, c);
addChildren(t, path, c, children);
}
}
}
}
void void
CommunitiesModel::initializeSidebar() CommunitiesModel::initializeSidebar()
{ {
beginResetModel(); beginResetModel();
tags_.clear(); tags_.clear();
spaceOrder_.clear(); spaceOrder_.tree.clear();
spaces_.clear(); spaces_.clear();
std::set<std::string> ts; std::set<std::string> ts;
std::vector<RoomInfo> tempSpaces;
std::set<std::string> isSpace;
std::map<std::string, std::set<std::string>> spaceChilds;
std::map<std::string, std::set<std::string>> spaceParents;
auto infos = cache::roomInfo(); auto infos = cache::roomInfo();
for (auto it = infos.begin(); it != infos.end(); it++) { for (auto it = infos.begin(); it != infos.end(); it++) {
if (it.value().is_space) { if (it.value().is_space) {
spaceOrder_.push_back(it.key());
spaces_[it.key()] = it.value(); spaces_[it.key()] = it.value();
isSpace.insert(it.key().toStdString());
} else { } else {
for (const auto &t : it.value().tags) { for (const auto &t : it.value().tags) {
if (t.find("u.") == 0 || t.find("m." == 0)) { if (t.find("u.") == 0 || t.find("m." == 0)) {
...@@ -149,6 +244,34 @@ CommunitiesModel::initializeSidebar() ...@@ -149,6 +244,34 @@ CommunitiesModel::initializeSidebar()
} }
} }
// NOTE(Nico): We build a forrest from the Directed Cyclic(!) Graph of spaces. To do that we
// start with orphan spaces at the top. This leaves out some space circles, but there is no good
// way to break that cycle imo anyway. Then we carefully walk a tree down from each root in our
// forrest, carefully checking not to run in a circle and get lost forever.
// TODO(Nico): Optimize this. We can do this with a lot fewer allocations and checks.
for (const auto &space : isSpace) {
spaceParents[space];
for (const auto &p : cache::client()->getParentRoomIds(space)) {
spaceParents[space].insert(p);
spaceChilds[p].insert(space);
}
}
temptree spacetree;
std::vector<std::string> path;
for (const auto &space : isSpace) {
if (!spaceParents[space].empty())
continue;
spacetree.children[space] = {};
}
for (const auto &space : spacetree.children) {
addChildren(spacetree, path, space.first, spaceChilds);
}
// NOTE(Nico): This flattens the tree into a list, preserving the depth at each element.
spacetree.flatten(spaceOrder_);
for (const auto &t : ts) for (const auto &t : ts)
tags_.push_back(QString::fromStdString(t)); tags_.push_back(QString::fromStdString(t));
...@@ -199,7 +322,7 @@ CommunitiesModel::sync(const mtx::responses::Sync &sync_) ...@@ -199,7 +322,7 @@ CommunitiesModel::sync(const mtx::responses::Sync &sync_)
} }
for (const auto &[roomid, room] : sync_.rooms.leave) { for (const auto &[roomid, room] : sync_.rooms.leave) {
(void)room; (void)room;
if (spaceOrder_.contains(QString::fromStdString(roomid))) if (spaces_.count(QString::fromStdString(roomid)))
tagsUpdated = true; tagsUpdated = true;
} }
for (const auto &e : sync_.account_data.events) { for (const auto &e : sync_.account_data.events) {
...@@ -228,8 +351,8 @@ CommunitiesModel::setCurrentTagId(QString tagId) ...@@ -228,8 +351,8 @@ CommunitiesModel::setCurrentTagId(QString tagId)
} }
} else if (tagId.startsWith("space:")) { } else if (tagId.startsWith("space:")) {
auto tag = tagId.mid(6); auto tag = tagId.mid(6);
for (const auto &t : spaceOrder_) { for (const auto &t : spaceOrder_.tree) {
if (t == tag) { if (t.name == tag) {
this->currentTagId_ = tagId; this->currentTagId_ = tagId;
emit currentTagIdChanged(currentTagId_); emit currentTagIdChanged(currentTagId_);
return; return;
...@@ -271,3 +394,88 @@ CommunitiesModel::toggleTagId(QString tagId) ...@@ -271,3 +394,88 @@ CommunitiesModel::toggleTagId(QString tagId)
emit hiddenTagsChanged(); emit hiddenTagsChanged();
} }
FilteredCommunitiesModel::FilteredCommunitiesModel(CommunitiesModel *model, QObject *parent)
: QSortFilterProxyModel(parent)
{
setSourceModel(model);
setDynamicSortFilter(true);
sort(0);
}
namespace {
enum Categories
{
World,
Direct,
Favourites,
Server,
LowPrio,
Space,
UserTag,
};
Categories
tagIdToCat(QString tagId)
{
if (tagId.isEmpty())
return World;
else if (tagId == "dm")
return Direct;
else if (tagId == "tag:m.favourite")
return Favourites;
else if (tagId == "tag:m.server_notice")
return Server;
else if (tagId == "tag:m.lowpriority")
return LowPrio;
else if (tagId.startsWith("space:"))
return Space;
else
return UserTag;
}
}
bool
FilteredCommunitiesModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
nhlog::ui()->debug("lessThan");
QModelIndex const left_idx = sourceModel()->index(left.row(), 0, QModelIndex());
QModelIndex const right_idx = sourceModel()->index(right.row(), 0, QModelIndex());
Categories leftCat = tagIdToCat(sourceModel()->data(left_idx, CommunitiesModel::Id).toString());
Categories rightCat =
tagIdToCat(sourceModel()->data(right_idx, CommunitiesModel::Id).toString());
if (leftCat != rightCat)
return leftCat < rightCat;
if (leftCat == Space) {
return left.row() < right.row();
}
QString leftName = sourceModel()->data(left_idx, CommunitiesModel::DisplayName).toString();
QString rightName = sourceModel()->data(right_idx, CommunitiesModel::DisplayName).toString();
return leftName.compare(rightName, Qt::CaseInsensitive) < 0;
}
bool
FilteredCommunitiesModel::filterAcceptsRow(int sourceRow, const QModelIndex &) const
{
CommunitiesModel *m = qobject_cast<CommunitiesModel *>(this->sourceModel());
if (!m)
return true;
if (sourceRow < 2 || sourceRow - 2 >= m->spaceOrder_.size())
return true;
auto idx = sourceRow - 2;
while (idx >= 0 && m->spaceOrder_.tree[idx].depth > 0) {
idx = m->spaceOrder_.parent(idx);
if (idx >= 0 && m->spaceOrder_.tree.at(idx).collapsed)
return false;
}
return true;
}
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QHash> #include <QHash>
#include <QSortFilterProxyModel>
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
...@@ -13,6 +14,18 @@ ...@@ -13,6 +14,18 @@
#include "CacheStructs.h" #include "CacheStructs.h"
class CommunitiesModel;
class FilteredCommunitiesModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
FilteredCommunitiesModel(CommunitiesModel *model, QObject *parent = nullptr);
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
bool filterAcceptsRow(int sourceRow, const QModelIndex &) const override;
};
class CommunitiesModel : public QAbstractListModel class CommunitiesModel : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
...@@ -27,11 +40,59 @@ public: ...@@ -27,11 +40,59 @@ public:
AvatarUrl = Qt::UserRole, AvatarUrl = Qt::UserRole,
DisplayName, DisplayName,
Tooltip, Tooltip,
ChildrenHidden, Collapsed,
Collapsible,
Hidden, Hidden,
Parent,
Depth,
Id, Id,
}; };
struct FlatTree
{
struct Elem
{
QString name;
int depth = 0;
bool collapsed = false;
};
std::vector<Elem> tree;
int size() const { return static_cast<int>(tree.size()); }
int indexOf(const QString &s) const
{
for (int i = 0; i < size(); i++)
if (tree[i].name == s)
return i;
return -1;
}
int lastChild(int index) const
{
if (index >= size() || index < 0)
return index;
const auto depth = tree[index].depth;
int i = index + 1;
for (; i < size(); i++)
if (tree[i].depth == depth)
break;
return i - 1;
}
int parent(int index) const
{
if (index >= size() || index < 0)
return -1;
const auto depth = tree[index].depth;
if (depth == 0)
return -1;
int i = index - 1;
for (; i >= 0; i--)
if (tree[i].depth < depth)
break;
return i;
}
};
CommunitiesModel(QObject *parent = nullptr); CommunitiesModel(QObject *parent = nullptr);
QHash<int, QByteArray> roleNames() const override; QHash<int, QByteArray> roleNames() const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override int rowCount(const QModelIndex &parent = QModelIndex()) const override
...@@ -40,6 +101,7 @@ public: ...@@ -40,6 +101,7 @@ public:
return 2 + tags_.size() + spaceOrder_.size(); return 2 + tags_.size() + spaceOrder_.size();
} }
QVariant data(const QModelIndex &index, int role) const override; QVariant data(const QModelIndex &index, int role) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
public slots: public slots:
void initializeSidebar(); void initializeSidebar();
...@@ -63,6 +125,7 @@ public slots: ...@@ -63,6 +125,7 @@ public slots:
return tagsWD; return tagsWD;
} }
void toggleTagId(QString tagId); void toggleTagId(QString tagId);
FilteredCommunitiesModel *filtered() { return new FilteredCommunitiesModel(this, this); }
signals: signals:
void currentTagIdChanged(QString tagId); void currentTagIdChanged(QString tagId);
...@@ -73,6 +136,8 @@ private: ...@@ -73,6 +136,8 @@ private:
QStringList tags_; QStringList tags_;
QString currentTagId_; QString currentTagId_;
QStringList hiddentTagIds_; QStringList hiddentTagIds_;
QStringList spaceOrder_; FlatTree spaceOrder_;
std::map<QString, RoomInfo> spaces_; std::map<QString, RoomInfo> spaces_;
friend class FilteredCommunitiesModel;
}; };
...@@ -260,6 +260,13 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par ...@@ -260,6 +260,13 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
qRegisterMetaType<mtx::events::collections::TimelineEvents>(); qRegisterMetaType<mtx::events::collections::TimelineEvents>();
qRegisterMetaType<std::vector<DeviceInfo>>(); qRegisterMetaType<std::vector<DeviceInfo>>();
qmlRegisterUncreatableType<FilteredCommunitiesModel>(
"im.nheko",
1,
0,
"FilteredCommunitiesModel",
"Use Communities.filtered() to create a FilteredCommunitiesModel");
qmlRegisterType<emoji::EmojiModel>("im.nheko.EmojiModel", 1, 0, "EmojiModel"); qmlRegisterType<emoji::EmojiModel>("im.nheko.EmojiModel", 1, 0, "EmojiModel");
qmlRegisterUncreatableType<emoji::Emoji>( qmlRegisterUncreatableType<emoji::Emoji>(
"im.nheko.EmojiModel", 1, 0, "Emoji", "Used by emoji models"); "im.nheko.EmojiModel", 1, 0, "Emoji", "Used by emoji models");
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment