Skip to content

Commit

Permalink
refactor: support cross-folder drag and drop
Browse files Browse the repository at this point in the history
支持跨文件夹拖拽。(实际的拖拽功能仍未启用)

重构以修正之前实现中,通过绑定文件夹编号形式进行的文件夹模型关联。
之前的做法在出现文件夹被删除的情况后就会出问题(例如存在 1,2,3 后
删除了文件夹 2,此时第二个文件夹的编号就不再是 2 而是 3)。

Log:
  • Loading branch information
BLumia committed Nov 22, 2023
1 parent 7c363bb commit 9ba5603
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 31 deletions.
6 changes: 5 additions & 1 deletion src/models/itemspage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ void ItemsPage::moveItem(int from_page, int from_index, int to_page, int to_inde
}
}

void ItemsPage::removeItem(const QString id, bool removePageIfPageIsEmpty)
bool ItemsPage::removeItem(const QString id, bool removePageIfPageIsEmpty)
{
int page, idx;
std::tie(page, idx) = findItem(id);
Expand All @@ -150,7 +150,11 @@ void ItemsPage::removeItem(const QString id, bool removePageIfPageIsEmpty)
m_pages.removeAt(page);
emit pageCountChanged();
}

return true;
}

return false;
}

// <page, index>
Expand Down
2 changes: 1 addition & 1 deletion src/models/itemspage.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class ItemsPage : public QObject
void appendItem(const QString id, int page = -1);
void insertItem(const QString id, int page, int pos = 0);
void moveItem(int from_page, int from_index, int to_page, int to_index);
void removeItem(const QString id, bool removePageIfPageIsEmpty = true);
bool removeItem(const QString id, bool removePageIfPageIsEmpty = true);

std::tuple<int, int> findItem(const QString & id) const;
bool contains(const QString & id) const;
Expand Down
113 changes: 86 additions & 27 deletions src/models/multipageproxymodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,32 +37,47 @@ void MultipageProxyModel::commitDndOperation(const QString &dragId, const QStrin

if (op != DndOperation::DndJoin) {
// move to dropId's front or back
// DnD can only happen in the same folder
Q_ASSERT(std::get<0>(dragOrigPos) == std::get<0>(dropOrigPos));
ItemsPage * folder = folderById(std::get<0>(dropOrigPos));
const int dragOrigPage = std::get<1>(dragOrigPos);
const int dropOrigPage = std::get<1>(dropOrigPos);
// FIXME: drop position not correct
folder->moveItem(dragOrigPage, std::get<2>(dragOrigPos), dropOrigPage, std::get<2>(dropOrigPos));
if (std::get<0>(dragOrigPos) == std::get<0>(dropOrigPos)) {
// same folder item re-arrangement
ItemsPage * folder = folderById(std::get<0>(dropOrigPos));
const int dragOrigPage = std::get<1>(dragOrigPos);
const int dropOrigPage = std::get<1>(dropOrigPos);
// FIXME: drop position not correct
folder->moveItem(dragOrigPage, std::get<2>(dragOrigPos), dropOrigPage, std::get<2>(dropOrigPos));
} else {
// different folder item arrangement
ItemsPage * srcFolder = folderById(std::get<0>(dragOrigPos));
ItemsPage * dstFolder = folderById(std::get<0>(dropOrigPos));
srcFolder->removeItem(dragId);
if (srcFolder->pageCount() == 0) {
// FIXME: crash
removeFolder(QString::number(std::get<0>(dragOrigPos)));
}
dstFolder->insertItem(dragId, std::get<1>(dropOrigPos), std::get<2>(dropOrigPos));
}
} else {
if (dragId.startsWith("internal/folders/")) return; // cannot drag folder onto something
if (std::get<0>(dropOrigPos) != 0) return; // folder inside folder is not allowed
if (dropId.startsWith("internal/folders/")) {
// drop into existing folder
m_topLevel->removeItem(dragId);
ItemsPage * srcFolder = folderById(std::get<0>(dragOrigPos));
srcFolder->removeItem(dragId);
if (srcFolder->pageCount() == 0) {
// FIXME: crash
removeFolder(QString::number(std::get<0>(dragOrigPos)));
}
m_folders.value(dropId)->appendItem(dragId);
} else {
// make a new folder, move two items into the folder
int folderCount = m_folders.count();
QString folderNumStr(QString::number(folderCount + 1));
ItemsPage * folder = createFolder(folderNumStr);
QString folderId = findAvailableFolderId();
ItemsPage * folder = createFolder(folderId);
folder->appendPage({dragId, dropId});
AppItem * dropItem = AppsModel::instance().itemFromDesktopId(dropId);
AppItem::DDECategories dropCategories = AppItem::DDECategories(CategoryUtils::parseBestMatchedCategory(dropItem->categories()));
folder->setName("internal/category/" + QString::number(dropCategories));
m_topLevel->removeItem(dragId);
m_topLevel->removeItem(dropId);
m_topLevel->insertItem("internal/folders/" + folderNumStr, std::get<1>(dropOrigPos), std::get<2>(dropOrigPos));
m_topLevel->insertItem(folderId, std::get<1>(dropOrigPos), std::get<2>(dropOrigPos));
}
}

Expand Down Expand Up @@ -112,7 +127,7 @@ QVariant MultipageProxyModel::data(const QModelIndex &index, int role) const
}
} else {
// a folder
QString id("internal/folders/" + QString::number(idx + 1));
QString id = m_folderIndexes[idx];
int folder, page, idx;
if (role >= AppsModel::ProxyModelExtendedRole && role != IconsNameRole) {
std::tie(folder, page, idx) = findItem(id, true);
Expand Down Expand Up @@ -231,14 +246,15 @@ void MultipageProxyModel::saveItemArrangementToUserData()
}
itemArrangementSettings.endGroup();

for (int i = 1; i <= m_folders.count(); i++) {
itemArrangementSettings.beginGroup("fullscreen/" + QString::number(i));
ItemsPage * page = m_folders.value("internal/folders/" + QString::number(i));
for (int i = 0; i < m_folderIndexes.count(); i++) {
QString id = m_folderIndexes[i];
itemArrangementSettings.beginGroup("fullscreen/" + id.mid(17));
ItemsPage * page = m_folders.value(m_folderIndexes[i]);
int pageCount = page->pageCount();
itemArrangementSettings.setValue("name", page->name());
itemArrangementSettings.setValue("pageCount", pageCount);
for (int i = 0; i < pageCount; i++) {
itemArrangementSettings.setValue(QString::asprintf("pageItems/%d", i), page->items(i));
for (int j = 0; j < pageCount; j++) {
itemArrangementSettings.setValue(QString::asprintf("pageItems/%d", j), page->items(j));
}
itemArrangementSettings.endGroup();
}
Expand All @@ -254,9 +270,12 @@ std::tuple<int, int, int> MultipageProxyModel::findItem(const QString &id, bool
if (page != -1) return std::make_tuple(0, page, idx);

if (!searchTopLevelOnly) {
for (int i = 1; i <= m_folders.count(); i++) {
std::tie(page, idx) = m_folders["internal/folders/" + QString::number(i)]->findItem(id);
if (page != -1) return std::make_tuple(i, page, idx);
for (const QString & folderId : qAsConst(m_folderIndexes)) {
std::tie(page, idx) = m_folders[folderId]->findItem(id);
if (page != -1) {
int i = m_folderIndexes.indexOf(folderId) + 1;
return std::make_tuple(i, page, idx);
}
}
}

Expand All @@ -276,6 +295,7 @@ void MultipageProxyModel::onSourceModelChanged()
std::tie(folder, std::ignore, std::ignore) = findItem(desktopId);
if (folder == -1) {
// qDebug() << desktopId;
findItem(desktopId);
m_topLevel->appendItem(desktopId);
}
}
Expand All @@ -284,24 +304,63 @@ void MultipageProxyModel::onSourceModelChanged()

saveItemArrangementToUserData();

// endResetModel();
// endResetModel();
}

ItemsPage *MultipageProxyModel::createFolder(const QString &idNumber)
int MultipageProxyModel::indexById(const QString &id)
{
QString fullId("internal/folders/" + idNumber);
Q_ASSERT(!m_folders.contains(fullId));
if (id.startsWith("internal/folders/")) {
int idx = m_folderIndexes.indexOf(id) + 1;
return (sourceModel() ? sourceModel()->rowCount() : 0) + idx;
} else {
QModelIndexList results = sourceModel()->match(sourceModel()->index(0, 0), AppItem::DesktopIdRole, id);
if (results.count() > 0) {
return results.constFirst().row();
}
return -1;
}
}

QString MultipageProxyModel::findAvailableFolderId()
{
int idNumber = 0;
QString fullId;
do {
idNumber++;
fullId = QStringLiteral("internal/folders/%1").arg(idNumber);
} while (m_folders.contains(fullId));

Q_ASSERT(idNumber != 0); // 0 is reserved for top level.
return fullId;
}

ItemsPage *MultipageProxyModel::createFolder(const QString &id)
{
Q_ASSERT(!id.isEmpty());
QString fullId(id.startsWith("internal/folders/") ? id : QStringLiteral("internal/folders/%1").arg(id));
Q_ASSERT(!m_folderIndexes.contains(fullId));

// int insertTo = rowCount(QModelIndex());
// beginInsertRows(QModelIndex(), insertTo, insertTo);
beginInsertRows(QModelIndex(), rowCount(QModelIndex()), rowCount(QModelIndex()));
ItemsPage * page = new ItemsPage(4 * 3, this);
m_folders.insert(fullId, page);
m_folderIndexes.append(fullId);
endInsertRows();

return page;
}

void MultipageProxyModel::removeFolder(const QString &idNumber)
{
QString fullId("internal/folders/" + idNumber);
Q_ASSERT(m_folders.contains(fullId));

int idx = indexById(fullId);
beginRemoveRows(QModelIndex(), idx, idx);
m_folders.remove(fullId);
m_folderIndexes.removeOne(fullId);
endRemoveRows();
}

// get folder by id. 0 is top level, >=1 is folder
ItemsPage *MultipageProxyModel::folderById(int id)
{
Expand Down
7 changes: 5 additions & 2 deletions src/models/multipageproxymodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,14 @@ class MultipageProxyModel : public QIdentityProxyModel
std::tuple<int, int, int> findItem(const QString & id, bool searchTopLevelOnly = false) const;
void onSourceModelChanged();

ItemsPage * createFolder(const QString & idNumber);
int indexById(const QString & id);
QString findAvailableFolderId();
ItemsPage * createFolder(const QString & id);
void removeFolder(const QString & idNumber);
ItemsPage * folderById(int id);

// <folder-id, items-arrangement-data> folder-id: internal/folder/<id number>
ItemsPage * m_topLevel;
QHash<QString, ItemsPage *> m_folders;

QStringList m_folderIndexes;
};

0 comments on commit 9ba5603

Please sign in to comment.