From a85eaedaaea1eb5acac45fab1d304a5d2a8dbc93 Mon Sep 17 00:00:00 2001 From: Fushan Wen Date: Tue, 26 Apr 2022 12:35:28 +0800 Subject: [PATCH] libtaskmanager: Fix dragging launcher icon when separateLaunchers is false When separateLaunchers is false and there are two opened pinned tasks and one launcher icon, dragging the launcher icon to a position between the two pinned tasks always fails for the first time. The general idea of this fix is to make sure after every move operation, pinned launhcer item and the corresponding opened window still stay together. BUG: 448912 FIXED-IN: 5.26 --- libtaskmanager/launchertasksmodel.cpp | 43 +++++++++---- libtaskmanager/tasksmodel.cpp | 92 ++++++++++++++++++++------- 2 files changed, 101 insertions(+), 34 deletions(-) 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; }