Re-order spaces by dragging and dropping
Title
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
#include <Quotient/room.h>
|
#include <Quotient/room.h>
|
||||||
|
|
||||||
#include "neochatconnection.h"
|
#include "neochatconnection.h"
|
||||||
|
#include "neochatroom.h"
|
||||||
|
|
||||||
SpaceChildrenModel::SpaceChildrenModel(QObject *parent)
|
SpaceChildrenModel::SpaceChildrenModel(QObject *parent)
|
||||||
: QAbstractItemModel(parent)
|
: QAbstractItemModel(parent)
|
||||||
@@ -32,7 +33,8 @@ void SpaceChildrenModel::setSpace(NeoChatRoom *space)
|
|||||||
}
|
}
|
||||||
// disconnect the new room signal from the old connection in case it is different.
|
// disconnect the new room signal from the old connection in case it is different.
|
||||||
if (m_space != nullptr) {
|
if (m_space != nullptr) {
|
||||||
disconnect(m_space->connection(), &Quotient::Connection::loadedRoomState, this, nullptr);
|
m_space->connection()->disconnect(this);
|
||||||
|
m_space->disconnect(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_space = space;
|
m_space = space;
|
||||||
@@ -116,6 +118,11 @@ void SpaceChildrenModel::insertChildren(std::vector<Quotient::GetSpaceHierarchyJ
|
|||||||
if (!successorId.isEmpty()) {
|
if (!successorId.isEmpty()) {
|
||||||
m_replacedRooms += successorId;
|
m_replacedRooms += successorId;
|
||||||
}
|
}
|
||||||
|
if (dynamic_cast<NeoChatRoom *>(room)->isSpace()) {
|
||||||
|
connect(room, &Quotient::Room::changed, this, [this]() {
|
||||||
|
refreshModel();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (children[i].childrenState.size() > 0) {
|
if (children[i].childrenState.size() > 0) {
|
||||||
auto job = m_space->connection()->callApi<Quotient::GetSpaceHierarchyJob>(children[i].roomId, Quotient::none, Quotient::none, 1);
|
auto job = m_space->connection()->callApi<Quotient::GetSpaceHierarchyJob>(children[i].roomId, Quotient::none, Quotient::none, 1);
|
||||||
|
|||||||
@@ -60,4 +60,65 @@ bool SpaceChildSortFilterModel::filterAcceptsRow(int sourceRow, const QModelInde
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SpaceChildSortFilterModel::move(const QModelIndex ¤tIndex, const QModelIndex &targetIndex)
|
||||||
|
{
|
||||||
|
const auto rootSpace = dynamic_cast<SpaceChildrenModel *>(sourceModel())->space();
|
||||||
|
if (rootSpace == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto connection = rootSpace->connection();
|
||||||
|
|
||||||
|
const auto currentParent = currentIndex.parent();
|
||||||
|
auto targetParent = targetIndex.parent();
|
||||||
|
NeoChatRoom *currentParentSpace = nullptr;
|
||||||
|
if (!currentParent.isValid()) {
|
||||||
|
currentParentSpace = rootSpace;
|
||||||
|
} else {
|
||||||
|
currentParentSpace = static_cast<NeoChatRoom *>(connection->room(currentParent.data(SpaceChildrenModel::RoomIDRole).toString()));
|
||||||
|
}
|
||||||
|
NeoChatRoom *targetParentSpace = nullptr;
|
||||||
|
if (!targetParent.isValid()) {
|
||||||
|
targetParentSpace = rootSpace;
|
||||||
|
} else {
|
||||||
|
targetParentSpace = static_cast<NeoChatRoom *>(connection->room(targetParent.data(SpaceChildrenModel::RoomIDRole).toString()));
|
||||||
|
}
|
||||||
|
// If both parents are not resolvable to a room object we don't have the permissions
|
||||||
|
// required for this action.
|
||||||
|
if (currentParentSpace == nullptr || targetParentSpace == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto currentRow = currentIndex.row();
|
||||||
|
auto targetRow = targetIndex.row();
|
||||||
|
|
||||||
|
const auto moveRoomId = currentIndex.data(SpaceChildrenModel::RoomIDRole).toString();
|
||||||
|
auto targetRoom = static_cast<NeoChatRoom *>(connection->room(targetIndex.data(SpaceChildrenModel::RoomIDRole).toString()));
|
||||||
|
// If the target room is a space, assume we want to drop the room into it.
|
||||||
|
if (targetRoom != nullptr && targetRoom->isSpace()) {
|
||||||
|
targetParent = targetIndex;
|
||||||
|
targetParentSpace = targetRoom;
|
||||||
|
targetRow = rowCount(targetParent);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto newRowCount = rowCount(targetParent) + (currentParentSpace != targetParentSpace ? 1 : 0);
|
||||||
|
for (int i = 0; i < newRowCount; i++) {
|
||||||
|
if (currentParentSpace == targetParentSpace && i == currentRow) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetParentSpace->setChildOrder(index(i, 0, targetParent).data(SpaceChildrenModel::RoomIDRole).toString(),
|
||||||
|
QString::number(i > targetRow ? i + 1 : i, 36));
|
||||||
|
|
||||||
|
if (i == targetRow) {
|
||||||
|
if (currentParentSpace != targetParentSpace) {
|
||||||
|
currentParentSpace->removeChild(moveRoomId, true);
|
||||||
|
targetParentSpace->addChild(moveRoomId, true, false, false, QString::number(i + 1, 36));
|
||||||
|
} else {
|
||||||
|
targetParentSpace->setChildOrder(currentIndex.data(SpaceChildrenModel::RoomIDRole).toString(), QString::number(i + 1, 36));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#include "moc_spacechildsortfiltermodel.cpp"
|
#include "moc_spacechildsortfiltermodel.cpp"
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ protected:
|
|||||||
*/
|
*/
|
||||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||||
|
|
||||||
|
Q_INVOKABLE void move(const QModelIndex ¤tIndex, const QModelIndex &targetIndex);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void filterTextChanged();
|
void filterTextChanged();
|
||||||
|
|
||||||
|
|||||||
@@ -1329,7 +1329,7 @@ bool NeoChatRoom::childrenHaveHighlightNotifications() const
|
|||||||
return SpaceHierarchyCache::instance().spaceHasHighlightNotifications(id());
|
return SpaceHierarchyCache::instance().spaceHasHighlightNotifications(id());
|
||||||
}
|
}
|
||||||
|
|
||||||
void NeoChatRoom::addChild(const QString &childId, bool setChildParent, bool canonical, bool suggested)
|
void NeoChatRoom::addChild(const QString &childId, bool setChildParent, bool canonical, bool suggested, const QString &order)
|
||||||
{
|
{
|
||||||
if (!isSpace()) {
|
if (!isSpace()) {
|
||||||
return;
|
return;
|
||||||
@@ -1337,7 +1337,9 @@ void NeoChatRoom::addChild(const QString &childId, bool setChildParent, bool can
|
|||||||
if (!canSendEvent("m.space.child"_ls)) {
|
if (!canSendEvent("m.space.child"_ls)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setState("m.space.child"_ls, childId, QJsonObject{{QLatin1String("via"), QJsonArray{connection()->domain()}}, {"suggested"_ls, suggested}});
|
setState("m.space.child"_ls,
|
||||||
|
childId,
|
||||||
|
QJsonObject{{QLatin1String("via"), QJsonArray{connection()->domain()}}, {"suggested"_ls, suggested}, {"order"_ls, order}});
|
||||||
|
|
||||||
if (setChildParent) {
|
if (setChildParent) {
|
||||||
if (auto child = static_cast<NeoChatRoom *>(connection()->room(childId))) {
|
if (auto child = static_cast<NeoChatRoom *>(connection()->room(childId))) {
|
||||||
@@ -1403,6 +1405,28 @@ void NeoChatRoom::toggleChildSuggested(const QString &childId)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NeoChatRoom::setChildOrder(const QString &childId, const QString &order)
|
||||||
|
{
|
||||||
|
if (!isSpace()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!canSendEvent("m.space.child"_ls)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (const auto childEvent = currentState().get("m.space.child"_ls, childId)) {
|
||||||
|
auto content = childEvent->contentJson();
|
||||||
|
if (!content.contains("via"_ls)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (content.value("order"_ls).toString() == order) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
content.insert("order"_ls, order);
|
||||||
|
setState("m.space.child"_ls, childId, content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PushNotificationState::State NeoChatRoom::pushNotificationState() const
|
PushNotificationState::State NeoChatRoom::pushNotificationState() const
|
||||||
{
|
{
|
||||||
return m_currentPushNotificationState;
|
return m_currentPushNotificationState;
|
||||||
|
|||||||
@@ -560,7 +560,7 @@ public:
|
|||||||
* Will fail if the user doesn't have the required privileges or this room is
|
* Will fail if the user doesn't have the required privileges or this room is
|
||||||
* not a space.
|
* not a space.
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE void addChild(const QString &childId, bool setChildParent = false, bool canonical = false, bool suggested = false);
|
Q_INVOKABLE void addChild(const QString &childId, bool setChildParent = false, bool canonical = false, bool suggested = false, const QString &order = {});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Remove the given room as a child.
|
* @brief Remove the given room as a child.
|
||||||
@@ -583,6 +583,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
Q_INVOKABLE void toggleChildSuggested(const QString &childId);
|
Q_INVOKABLE void toggleChildSuggested(const QString &childId);
|
||||||
|
|
||||||
|
void setChildOrder(const QString &childId, const QString &order = {});
|
||||||
|
|
||||||
bool isInvite() const;
|
bool isInvite() const;
|
||||||
|
|
||||||
bool readOnly() const;
|
bool readOnly() const;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ Item {
|
|||||||
required property bool expanded
|
required property bool expanded
|
||||||
required property int hasChildren
|
required property int hasChildren
|
||||||
required property int depth
|
required property int depth
|
||||||
|
required property int row
|
||||||
required property string roomId
|
required property string roomId
|
||||||
required property string displayName
|
required property string displayName
|
||||||
required property url avatarUrl
|
required property url avatarUrl
|
||||||
@@ -36,10 +37,17 @@ Item {
|
|||||||
signal createRoom
|
signal createRoom
|
||||||
|
|
||||||
Delegates.RoundedItemDelegate {
|
Delegates.RoundedItemDelegate {
|
||||||
anchors.centerIn: root
|
id: mainDelegate
|
||||||
|
property int row: root.row
|
||||||
|
|
||||||
|
anchors.horizontalCenter: root.horizontalCenter
|
||||||
|
anchors.verticalCenter: root.verticalCenter
|
||||||
width: sizeHelper.currentWidth
|
width: sizeHelper.currentWidth
|
||||||
|
|
||||||
|
highlighted: dropArea.containsDrag
|
||||||
|
|
||||||
contentItem: RowLayout {
|
contentItem: RowLayout {
|
||||||
|
z: 1
|
||||||
spacing: Kirigami.Units.largeSpacing
|
spacing: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
@@ -140,15 +148,55 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TapHandler {
|
MouseArea {
|
||||||
onTapped: {
|
id: dragArea
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
drag.target: mainDelegate
|
||||||
|
drag.axis: Drag.YAxis
|
||||||
|
|
||||||
|
drag.onActiveChanged: {
|
||||||
|
if (!dragArea.drag.active) {
|
||||||
|
mainDelegate.Drag.drop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
if (root.isSpace) {
|
if (root.isSpace) {
|
||||||
root.treeView.toggleExpanded(row);
|
root.treeView.toggleExpanded(root.row);
|
||||||
} else {
|
} else {
|
||||||
RoomManager.resolveResource(root.roomId, root.isJoined ? "" : "join");
|
RoomManager.resolveResource(root.roomId, root.isJoined ? "" : "join");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
when: mainDelegate.Drag.active && root.parentRoom.canSendState("m.space.child")
|
||||||
|
ParentChange {
|
||||||
|
target: mainDelegate
|
||||||
|
parent: root.treeView
|
||||||
|
}
|
||||||
|
|
||||||
|
AnchorChanges {
|
||||||
|
target: mainDelegate
|
||||||
|
anchors.horizontalCenter: undefined
|
||||||
|
anchors.verticalCenter: undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
Drag.active: dragArea.drag.active
|
||||||
|
Drag.hotSpot.x: mainDelegate.width / 2
|
||||||
|
Drag.hotSpot.y: Kirigami.Units.smallSpacing
|
||||||
|
}
|
||||||
|
|
||||||
|
DropArea {
|
||||||
|
id: dropArea
|
||||||
|
anchors.fill: parent
|
||||||
|
onDropped: (drag) => {
|
||||||
|
root.treeView.model.move(root.treeView.index(drag.source.row, 0), root.treeView.index(root.row, 0))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateSizeHelper {
|
DelegateSizeHelper {
|
||||||
|
|||||||
Reference in New Issue
Block a user