diff --git a/panels/dock/tray/package/TrayContainer.qml b/panels/dock/tray/package/TrayContainer.qml index 1c4e57e72..7ca315ca1 100644 --- a/panels/dock/tray/package/TrayContainer.qml +++ b/panels/dock/tray/package/TrayContainer.qml @@ -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 @@ -172,7 +172,7 @@ Item { // 检查当前悬停位置是否是禁止拖拽的插件 let modelIndex = DDT.TraySortOrderModel.getModelIndexByVisualIndex(currentItemIndex) - let sectionType =root.model.data(modelIndex, DDT.TraySortOrderModel.SectionTypeRole) + let sectionType = root.model.data(modelIndex, DDT.TraySortOrderModel.SectionTypeRole) if (sectionType === "fixed") { dragEvent.accepted = false return @@ -189,34 +189,45 @@ Item { } } - // TODO: If this method is used in the stash area, it will cause the drag state to be terminated when dragging to the tray area - if (!isStash) { - // 根据 ActionShowStashDelegate 的显示状态动态改变条件 - let shouldAllowDrop = showStashActionVisible ? (dropHoverIndex !== 0) : (dropHoverIndex !== -1) - if (shouldAllowDrop) { - dropTrayTimer.handleDrop = function() { - if (isDropped || dragExited) return - DDT.TraySortOrderModel.dropToDockTray(surfaceId, Math.floor(currentItemIndex), isBefore) - } - dropTrayTimer.start() - } else if (!surfaceId.startsWith("application-tray")){ - dragEvent.accepted = false + // 根据 ActionShowStashDelegate 的显示状态动态改变条件 + let shouldAllowDrop = showStashActionVisible ? (dropHoverIndex !== 0) : (dropHoverIndex !== -1) + + // 使用暂存机制 + if (isStash && shouldAllowDrop) { + // 调用 stageDropPosition 来预览拖放位置,预留空位 + DDT.TraySortOrderModel.stageDropPosition(surfaceId, Math.floor(currentItemIndex)) + } else if (!isStash && shouldAllowDrop) { + dropTrayTimer.handleDrop = function() { + if (isDropped || dragExited) return + DDT.TraySortOrderModel.dropToDockTray(surfaceId, Math.floor(currentItemIndex), isBefore) } + dropTrayTimer.start() + } else if (!surfaceId.startsWith("application-tray")){ + dragEvent.accepted = false } } + onDropped: function (dropEvent) { isDropped = true let surfaceId = dropEvent.getDataAsString("text/x-dde-shell-tray-dnd-surfaceId") let dropIdx = DDT.TrayItemPositionManager.itemIndexByPoint(Qt.point(drag.x, drag.y)) let currentItemIndex = dropIdx.index let isBefore = dropIdx.isBefore - console.log("dropped", currentItemIndex, isBefore) - DDT.TraySortOrderModel.dropToDockTray(surfaceId, Math.floor(currentItemIndex), isBefore); + let isStash = dropEvent.getDataAsString("text/x-dde-shell-tray-dnd-sectionType") === "stashed" + console.log("dropped", currentItemIndex, isBefore, isStash) + + if (isStash) { + // 提交暂存的拖放位置 + DDT.TraySortOrderModel.commitStagedDrop() + } else { + DDT.TraySortOrderModel.dropToDockTray(surfaceId, Math.floor(currentItemIndex), isBefore); + } DDT.TraySortOrderModel.actionsAlwaysVisible = false } onExited: function () { dragExited = true + DDT.TraySortOrderModel.clearStagedDrop() // dragging from quickPanel, entered trayContainer, but not dropped in this area if (source !== "" && !isDropped) { dropTrayTimer.stop() diff --git a/panels/dock/tray/trayitempositionmanager.cpp b/panels/dock/tray/trayitempositionmanager.cpp index 80c45b3f5..346f8b1ad 100644 --- a/panels/dock/tray/trayitempositionmanager.cpp +++ b/panels/dock/tray/trayitempositionmanager.cpp @@ -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 @@ -119,6 +119,17 @@ void TrayItemPositionManager::layoutHealthCheck(int delayMs) qDebug() << "layout health check scheduled!"; } +void TrayItemPositionManager::clearRegisteredSizes() +{ + // Avoid emitting signal if there's nothing to clear + if (m_registeredItemsSize.isEmpty()) { + return; + } + + m_registeredItemsSize.clear(); + emit visualItemSizeChanged(); +} + TrayItemPositionManager::TrayItemPositionManager(QObject *parent) : QObject(parent) { diff --git a/panels/dock/tray/trayitempositionmanager.h b/panels/dock/tray/trayitempositionmanager.h index f2f5e9f86..da1c89cd0 100644 --- a/panels/dock/tray/trayitempositionmanager.h +++ b/panels/dock/tray/trayitempositionmanager.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 @@ -58,6 +58,7 @@ class TrayItemPositionManager : public QObject Qt::Orientation orientation() const; int dockHeight() const; Q_INVOKABLE void layoutHealthCheck(int delayMs = 200); + Q_INVOKABLE void clearRegisteredSizes(); signals: void orientationChanged(Qt::Orientation); diff --git a/panels/dock/tray/traysortordermodel.cpp b/panels/dock/tray/traysortordermodel.cpp index 6276417fe..805bd1ce9 100644 --- a/panels/dock/tray/traysortordermodel.cpp +++ b/panels/dock/tray/traysortordermodel.cpp @@ -1,9 +1,10 @@ -// 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 #include "traysortordermodel.h" #include "constants.h" +#include "trayitempositionmanager.h" #include #include @@ -361,6 +362,11 @@ void TraySortOrderModel::updateVisualIndexes() m_isUpdating = true; emit isUpdatingChanged(true); + // Clear registered sizes before re-assigning visual indexes + // This ensures that when items are repositioned, the empty placeholder + // will use the default item size instead of retaining the previous item's size + TrayItemPositionManager::instance().clearRegisteredSizes(); + for (int i = 0; i < rowCount(); i++) { item(i)->setData(-1, TraySortOrderModel::VisualIndexRole); } @@ -421,6 +427,7 @@ void TraySortOrderModel::updateVisualIndexes() if (itemVisible && dockVisible) { toogleCollapseActionVisible = true; if (!m_collapsed) { + reserveStagedDropSpace(currentVisualIndex); results[0]->setData(currentVisualIndex++, TraySortOrderModel::VisualIndexRole); } else { // When collapsed, collapsable items should be hidden (visualIndex = -1) @@ -434,6 +441,7 @@ void TraySortOrderModel::updateVisualIndexes() Q_ASSERT(!results.isEmpty()); results[0]->setData(toogleCollapseActionVisible, TraySortOrderModel::VisibilityRole); if (toogleCollapseActionVisible) { + reserveStagedDropSpace(currentVisualIndex); results[0]->setData(currentVisualIndex, TraySortOrderModel::VisualIndexRole); currentVisualIndex++; } @@ -450,6 +458,7 @@ void TraySortOrderModel::updateVisualIndexes() results[0]->setData(itemVisible, TraySortOrderModel::VisibilityRole); results[0]->setData(dockVisible, TraySortOrderModel::DockVisibleRole); if (itemVisible && dockVisible) { + reserveStagedDropSpace(currentVisualIndex); results[0]->setData(currentVisualIndex, TraySortOrderModel::VisualIndexRole); currentVisualIndex++; } @@ -459,6 +468,7 @@ void TraySortOrderModel::updateVisualIndexes() results = findItems("internal/action-toggle-quick-settings"); Q_ASSERT(!results.isEmpty()); results[0]->setData(SECTION_FIXED, TraySortOrderModel::SectionTypeRole); + reserveStagedDropSpace(currentVisualIndex); results[0]->setData(currentVisualIndex, TraySortOrderModel::VisualIndexRole); currentVisualIndex++; @@ -600,4 +610,56 @@ QModelIndex TraySortOrderModel::getModelIndexByVisualIndex(int visualIndex) cons return QModelIndex(); } +void TraySortOrderModel::reserveStagedDropSpace(int ¤tVisualIndex) +{ + if (!m_stagedSurfaceId.isEmpty() && currentVisualIndex == m_stagedVisualIndex) { + currentVisualIndex++; + } +} + +void TraySortOrderModel::stageDropPosition(const QString &surfaceId, int visualIndex) +{ + if (m_stagedSurfaceId == surfaceId && m_stagedVisualIndex == visualIndex) { + return; + } + + m_stagedSurfaceId = surfaceId; + m_stagedVisualIndex = visualIndex; + emit stagedDropChanged(); + + // Update visual indexes to show preview position + updateVisualIndexes(); +} + +void TraySortOrderModel::commitStagedDrop() +{ + if (m_stagedSurfaceId.isEmpty() || m_stagedVisualIndex < 0) { + return; + } + + // Reuse dropToDockTray logic for consistency + // isBefore is always true for staged drops (insert before the target position) + dropToDockTray(m_stagedSurfaceId, m_stagedVisualIndex, true); + + // Clear staged state (dropToDockTray already called updateVisualIndexes) + m_stagedSurfaceId.clear(); + m_stagedVisualIndex = -1; + emit stagedDropChanged(); +} + +void TraySortOrderModel::clearStagedDrop() +{ + // Avoid redundant work if there is no staged drop + if (m_stagedSurfaceId.isEmpty() && m_stagedVisualIndex < 0) { + return; + } + + m_stagedSurfaceId.clear(); + m_stagedVisualIndex = -1; + emit stagedDropChanged(); + + // Update visual indexes to remove preview + updateVisualIndexes(); +} + } diff --git a/panels/dock/tray/traysortordermodel.h b/panels/dock/tray/traysortordermodel.h index 2f24a9825..69b1a57ef 100644 --- a/panels/dock/tray/traysortordermodel.h +++ b/panels/dock/tray/traysortordermodel.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 @@ -27,6 +27,8 @@ class TraySortOrderModel : public QStandardItemModel Q_PROPERTY(bool actionsAlwaysVisible MEMBER m_actionsAlwaysVisible NOTIFY actionsAlwaysVisibleChanged) Q_PROPERTY(bool isUpdating MEMBER m_isUpdating NOTIFY isUpdatingChanged) Q_PROPERTY(QList availableSurfaces MEMBER m_availableSurfaces NOTIFY availableSurfacesChanged) + Q_PROPERTY(QString stagedSurfaceId MEMBER m_stagedSurfaceId NOTIFY stagedDropChanged) + Q_PROPERTY(int stagedVisualIndex MEMBER m_stagedVisualIndex NOTIFY stagedDropChanged) public: // enum SectionTypes { // TrayAction, @@ -67,6 +69,11 @@ class TraySortOrderModel : public QStandardItemModel Q_INVOKABLE void setDockVisible(const QString & surfaceId, bool visible); Q_INVOKABLE bool isDockVisible(const QString &surfaceId) const; Q_INVOKABLE QModelIndex getModelIndexByVisualIndex(int visualIndex) const; + + // Staged drop methods for drag preview + Q_INVOKABLE void stageDropPosition(const QString &surfaceId, int visualIndex); + Q_INVOKABLE void commitStagedDrop(); + Q_INVOKABLE void clearStagedDrop(); signals: void collapsedChanged(bool); @@ -75,6 +82,7 @@ class TraySortOrderModel : public QStandardItemModel void isUpdatingChanged(bool); void visualItemCountChanged(int); void availableSurfacesChanged(const QList &); + void stagedDropChanged(); private: int m_visualItemCount = 0; @@ -94,9 +102,16 @@ class TraySortOrderModel : public QStandardItemModel QStringList m_hiddenIds; // surface IDs that should be hidden from dock tray but keep VisibilityRole true. QStringList m_dockHiddenIds; + + // Staged drop state for drag preview + QString m_stagedSurfaceId; + int m_stagedVisualIndex = -1; QStandardItem * findItemByVisualIndex(int visualIndex, VisualSections visualSection) const; QStringList * getSection(const QString & sectionType); + + // Helper function for reserving space during staged drop + void reserveStagedDropSpace(int ¤tVisualIndex); QString findSection(const QString &surfaceId, const QString &fallback, const QStringList &forbiddenSections, int pluginFlags); void registerToSection(const QString & surfaceId, const QString & sectionType); QStandardItem *createTrayItem(const QString &name,