diff --git a/panels/dock/taskmanager/dockglobalelementmodel.cpp b/panels/dock/taskmanager/dockglobalelementmodel.cpp index 0d452b43f..2f0656af3 100644 --- a/panels/dock/taskmanager/dockglobalelementmodel.cpp +++ b/panels/dock/taskmanager/dockglobalelementmodel.cpp @@ -29,6 +29,8 @@ DockGlobalElementModel::DockGlobalElementModel(QAbstractItemModel *appsModel, Do , m_activeAppModel(activeAppModel) { connect(TaskManagerSettings::instance(), &TaskManagerSettings::dockedElementsChanged, this, &DockGlobalElementModel::loadDockedElements); + connect(TaskManagerSettings::instance(), &TaskManagerSettings::windowSplitChanged, this, &DockGlobalElementModel::groupItemsByApp); + connect( m_appsModel, &QAbstractItemModel::rowsRemoved, @@ -96,10 +98,13 @@ DockGlobalElementModel::DockGlobalElementModel(QAbstractItemModel *appsModel, Do } // There are already windows for this app: insert the new window - // right after the last existing one so that all windows stay together. + // right after the last (rightmost) existing one. + // Search the entire list since windows may not be consecutive after drag reorder. auto lastIt = firstIt; - while (lastIt + 1 != m_data.end() && std::get<0>(*(lastIt + 1)) == desktopId) { - ++lastIt; + for (auto it = firstIt + 1; it != m_data.end(); ++it) { + if (std::get<0>(*it) == desktopId) { + lastIt = it; + } } auto insertRow = (lastIt - m_data.begin()) + 1; @@ -487,4 +492,52 @@ void DockGlobalElementModel::requestWindowsView(const QModelIndexList &indexes) { Q_UNUSED(indexes) } + +void DockGlobalElementModel::moveItem(int from, int to) +{ + if (from < 0 || from >= m_data.size() || to < 0 || to >= m_data.size() || from == to) + return; + + int destRow = from < to ? to + 1 : to; + + if (!beginMoveRows(QModelIndex(), from, from, QModelIndex(), destRow)) + return; + + m_data.move(from, to); + endMoveRows(); +} + +void DockGlobalElementModel::groupItemsByApp() +{ + if (m_data.isEmpty()) + return; + + if (TaskManagerSettings::instance()->isWindowSplit()) + return; + + for (int i = 0; i < m_data.size(); ++i) { + const QString currentId = std::get<0>(m_data.at(i)); + + int insertPos = i + 1; + + while (insertPos < m_data.size() && std::get<0>(m_data.at(insertPos)) == currentId) { + ++insertPos; + } + + for (int j = insertPos; j < m_data.size(); ++j) { + if (std::get<0>(m_data.at(j)) != currentId) + continue; + + int destRow = insertPos < j ? insertPos : insertPos + 1; + if (!beginMoveRows(QModelIndex(), j, j, QModelIndex(), destRow)) + continue; + m_data.move(j, insertPos); + endMoveRows(); + + ++insertPos; + } + + i = insertPos - 1; + } +} } diff --git a/panels/dock/taskmanager/dockglobalelementmodel.h b/panels/dock/taskmanager/dockglobalelementmodel.h index 15db77425..64a991bcf 100644 --- a/panels/dock/taskmanager/dockglobalelementmodel.h +++ b/panels/dock/taskmanager/dockglobalelementmodel.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #pragma once @@ -36,12 +36,15 @@ class DockGlobalElementModel : public QAbstractListModel, public AbstractTaskMan void requestWindowsView(const QModelIndexList &indexes) const override; + void moveItem(int from, int to); + public slots: void initDockedElements(bool unused); private: void loadDockedElements(); QString getMenus(const QModelIndex &index) const; + void groupItemsByApp(); private: // id, model, and pos diff --git a/panels/dock/taskmanager/dockitemmodel.cpp b/panels/dock/taskmanager/dockitemmodel.cpp index 3c12128ee..d05965260 100644 --- a/panels/dock/taskmanager/dockitemmodel.cpp +++ b/panels/dock/taskmanager/dockitemmodel.cpp @@ -79,6 +79,13 @@ void DockItemModel::setSourceModel(QAbstractItemModel *model) auto last = bottomRight.row(); Q_EMIT dataChanged(index(first, 0), index(last, 0), roles); }); + connect(sourceModel(), &QAbstractItemModel::rowsMoved, this, [this](const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row) { + Q_UNUSED(destination) + if (parent.isValid() || m_isUpdating) + return; + beginMoveRows(QModelIndex(), start, end, QModelIndex(), row); + endMoveRows(); + }); auto bottomRight = this->index(std::min(currentCount, newCount), 0); Q_EMIT dataChanged(index(0, 0), bottomRight); diff --git a/panels/dock/taskmanager/package/TaskManager.qml b/panels/dock/taskmanager/package/TaskManager.qml index 82740dfea..f4f82d2c6 100644 --- a/panels/dock/taskmanager/package/TaskManager.qml +++ b/panels/dock/taskmanager/package/TaskManager.qml @@ -262,7 +262,11 @@ ContainmentItem { let appId = taskmanager.Applet.desktopIdToAppId(launcherDndDesktopId) let currentIndex = taskmanager.Applet.windowSplit ? taskmanager.findAppIndexByWindow(appId, launcherDndWinId) : taskmanager.findAppIndex(appId) if (currentIndex !== -1 && targetIndex !== -1 && currentIndex !== targetIndex) { - visualModel.items.move(currentIndex, targetIndex) + if (taskmanager.Applet.windowSplit) { + taskmanager.Applet.moveItem(currentIndex, targetIndex) + } else { + visualModel.items.move(currentIndex, targetIndex) + } } } @@ -273,7 +277,11 @@ ContainmentItem { let appId = taskmanager.Applet.desktopIdToAppId(launcherDndDesktopId) let currentIndex = taskmanager.Applet.windowSplit ? taskmanager.findAppIndexByWindow(appId, launcherDndWinId) : taskmanager.findAppIndex(appId) if (currentIndex !== -1 && targetIndex !== -1 && currentIndex !== targetIndex) { - visualModel.items.move(currentIndex, targetIndex) + if (taskmanager.Applet.windowSplit) { + taskmanager.Applet.moveItem(currentIndex, targetIndex) + } else { + visualModel.items.move(currentIndex, targetIndex) + } } let appIds = [] for (let i = 0; i < visualModel.items.count; i++) { diff --git a/panels/dock/taskmanager/taskmanager.cpp b/panels/dock/taskmanager/taskmanager.cpp index ea9ac23b3..7d644e460 100644 --- a/panels/dock/taskmanager/taskmanager.cpp +++ b/panels/dock/taskmanager/taskmanager.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -484,6 +484,12 @@ void TaskManager::saveDockElementsOrder(const QStringList &appIds) TaskManagerSettings::instance()->setDockedElements(newDockedElements); } +void TaskManager::moveItem(int from, int to) +{ + if (m_dockGlobalElementModel) + m_dockGlobalElementModel->moveItem(from, to); +} + QString TaskManager::getTrashTipText() { const auto count = queryTrashCount(); diff --git a/panels/dock/taskmanager/taskmanager.h b/panels/dock/taskmanager/taskmanager.h index ac24158d4..07079746e 100644 --- a/panels/dock/taskmanager/taskmanager.h +++ b/panels/dock/taskmanager/taskmanager.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -100,6 +100,7 @@ class TaskManager : public DS_NAMESPACE::DContainment, public AbstractTaskManage Q_INVOKABLE void activateWindow(uint32_t windowID); Q_INVOKABLE void saveDockElementsOrder(const QStringList &appIds); + Q_INVOKABLE void moveItem(int from, int to); Q_INVOKABLE QString getTrashTipText(); Q_INVOKABLE bool isTrashEmpty() const;