diff --git a/libtaskmanager/launchertasksmodel.cpp b/libtaskmanager/launchertasksmodel.cpp index 7ac05fe5b..6760d0b0b 100644 --- a/libtaskmanager/launchertasksmodel.cpp +++ b/libtaskmanager/launchertasksmodel.cpp @@ -446,23 +446,42 @@ void LauncherTasksModel::setLauncherList(const QStringList &serializedLaunchers) } if (newLaunchersOrder != d->launchersOrder) { - // Use Remove/Insert to update the manual sort map in TasksModel - if (!d->launchersOrder.empty()) { - beginRemoveRows(QModelIndex(), 0, d->launchersOrder.size() - 1); + const bool isOrderChanged = std::all_of(newLaunchersOrder.cbegin(), + newLaunchersOrder.cend(), + [this](const QUrl &url) { + return d->launchersOrder.contains(url); + }) + && newLaunchersOrder.size() == d->launchersOrder.size(); + + if (isOrderChanged) { + for (int i = 0; i < newLaunchersOrder.size(); i++) { + int oldRow = d->launchersOrder.indexOf(newLaunchersOrder.at(i)); + + if (oldRow != i) { + beginMoveRows(QModelIndex(), oldRow, oldRow, QModelIndex(), i); + d->launchersOrder.move(oldRow, i); + endMoveRows(); + } + } + } else { + // Use Remove/Insert to update the manual sort map in TasksModel + if (!d->launchersOrder.empty()) { + beginRemoveRows(QModelIndex(), 0, d->launchersOrder.size() - 1); - d->launchersOrder.clear(); - d->activitiesForLauncher.clear(); + d->launchersOrder.clear(); + d->activitiesForLauncher.clear(); - endRemoveRows(); - } + endRemoveRows(); + } - if (!newLaunchersOrder.empty()) { - beginInsertRows(QModelIndex(), 0, newLaunchersOrder.size() - 1); + if (!newLaunchersOrder.empty()) { + beginInsertRows(QModelIndex(), 0, newLaunchersOrder.size() - 1); - d->launchersOrder = newLaunchersOrder; - d->activitiesForLauncher = newActivitiesForLauncher; + d->launchersOrder = newLaunchersOrder; + d->activitiesForLauncher = newActivitiesForLauncher; - endInsertRows(); + endInsertRows(); + } } Q_EMIT launcherListChanged(); diff --git a/libtaskmanager/tasksmodel.cpp b/libtaskmanager/tasksmodel.cpp index 361ffc8ce..caeb68203 100644 --- a/libtaskmanager/tasksmodel.cpp +++ b/libtaskmanager/tasksmodel.cpp @@ -1715,32 +1715,75 @@ bool TasksModel::move(int row, int newPos, const QModelIndex &parent) // Resort. d->forceResort(); - if (!d->separateLaunchers && isLauncherMove) { - const QModelIndex &idx = d->concatProxyModel->index(d->sortedPreFilterRows.at(newPos), 0); - const QUrl &launcherUrl = idx.data(AbstractTasksModel::LauncherUrlWithoutIcon).toUrl(); - - // Move launcher for launcher-backed task along with task if launchers - // are not being kept separate. - // We don't need to resort again because the launcher is implicitly hidden - // at this time. - if (!idx.data(AbstractTasksModel::IsLauncher).toBool()) { - const int launcherPos = d->launcherTasksModel->launcherPosition(launcherUrl); - const QModelIndex &launcherIndex = d->launcherTasksModel->index(launcherPos, 0); - const int sortIndex = d->sortedPreFilterRows.indexOf(d->concatProxyModel->mapFromSource(launcherIndex).row()); - d->sortedPreFilterRows.move(sortIndex, newPos); - // Otherwise move matching windows to after the launcher task (they are - // currently hidden but might be on another virtual desktop). - } else { - for (int i = (d->sortedPreFilterRows.count() - 1); i >= 0; --i) { - const QModelIndex &concatProxyIndex = d->concatProxyModel->index(d->sortedPreFilterRows.at(i), 0); + if (!d->separateLaunchers) { + if (isLauncherMove) { + const QModelIndex &idx = d->concatProxyModel->index(d->sortedPreFilterRows.at(newPos), 0); + const QUrl &launcherUrl = idx.data(AbstractTasksModel::LauncherUrlWithoutIcon).toUrl(); - if (launcherUrl == concatProxyIndex.data(AbstractTasksModel::LauncherUrlWithoutIcon).toUrl()) { - d->sortedPreFilterRows.move(i, newPos); + // Move launcher for launcher-backed task along with task if launchers + // are not being kept separate. + // We don't need to resort again because the launcher is implicitly hidden + // at this time. + if (!idx.data(AbstractTasksModel::IsLauncher).toBool()) { + const int launcherPos = d->launcherTasksModel->launcherPosition(launcherUrl); + const QModelIndex &launcherIndex = d->launcherTasksModel->index(launcherPos, 0); + const int sortIndex = d->sortedPreFilterRows.indexOf(d->concatProxyModel->mapFromSource(launcherIndex).row()); + d->sortedPreFilterRows.move(sortIndex, newPos); + + /* + * Before moving: + * [pinned 2 (launcher)] [pinned 2 (window)] [pinned 1 (launcher)] [pinned 1 (window)] + * After moving [pinned 1], sortedPreFilterRows may become: + * - row > newPos: [pinned 2 (launcher)] [pinned 1 (launcher)] [pinned 1 (window)] [pinned 2 (window)] + * - row < newPos: [pinned 2 (window)] [pinned 1 (window)] [pinned 1 (launcher)] [pinned 2 (launcher)] + * We need to move [pinned 2 (launcher)] to the left of [pinned 2 (window)] + */ + if (row > newPos && newPos - 1 >= 0 && newPos + 2 < d->sortedPreFilterRows.size()) { + const QModelIndex beforeIdx = d->concatProxyModel->index(d->sortedPreFilterRows.at(newPos - 1), 0); // [pinned 2 (launcher)] + const QModelIndex afterIdx = d->concatProxyModel->index(d->sortedPreFilterRows.at(newPos + 2), 0); // [pinned 2 (window)] + + if (appsMatch(beforeIdx, afterIdx)) { + // Move [pinned 2 (launcher)] before [pinned 2 (window)] + d->sortedPreFilterRows.move(newPos - 1, newPos + 2); + } + } else if (row < newPos && newPos - 2 >= 0 && newPos + 1 < d->sortedPreFilterRows.size()) { + const QModelIndex beforeIdx = d->concatProxyModel->index(d->sortedPreFilterRows.at(newPos - 2), 0); // [pinned 2 (window)] + const QModelIndex afterIdx = d->concatProxyModel->index(d->sortedPreFilterRows.at(newPos + 1), 0); // [pinned 2 (launcher)] - if (newPos > i) { - --newPos; + if (appsMatch(beforeIdx, afterIdx)) { + // Move [pinned 2 (launcher)] before [pinned 2 (window)] + d->sortedPreFilterRows.move(newPos + 1, newPos - 2); } } + // Otherwise move matching windows to after the launcher task (they are + // currently hidden but might be on another virtual desktop). + } else { + for (int i = (d->sortedPreFilterRows.count() - 1); i >= 0; --i) { + const QModelIndex &concatProxyIndex = d->concatProxyModel->index(d->sortedPreFilterRows.at(i), 0); + + if (launcherUrl == concatProxyIndex.data(AbstractTasksModel::LauncherUrlWithoutIcon).toUrl()) { + d->sortedPreFilterRows.move(i, newPos); + + if (newPos > i) { + --newPos; + } + } + } + } + } else if (newPos > 0 && newPos < d->sortedPreFilterRows.size() - 1) { + /* + * When dragging an unpinned task, a pinned task can also be moved. + * In this case, sortedPreFilterRows is like: + * - before moving: [pinned 1 (launcher item)] [pinned 1 (window)] [unpinned] + * - after moving: [pinned 1 (launcher item)] [unpinned] [pinned 1 (window)] + * So also check the indexes before and after the unpinned task. + */ + const QModelIndex beforeIdx = d->concatProxyModel->index(d->sortedPreFilterRows.at(newPos - 1), 0); + const QModelIndex afterIdx = d->concatProxyModel->index(d->sortedPreFilterRows.at(newPos + 1), 0); + + if (appsMatch(beforeIdx, afterIdx)) { + // after adjusting: [unpinned] [pinned 1 (launcher item)] [pinned 1] + d->sortedPreFilterRows.move(newPos, newPos + (row < newPos ? 1 : -1)); } } } @@ -1818,6 +1861,11 @@ void TasksModel::syncLaunchers() } setLauncherList(sortedShownLaunchers.values() + sortedHiddenLaunchers); + + // The accepted rows are outdated after the item order is changed + invalidateFilter(); + d->forceResort(); + d->launcherSortingDirty = false; }