From 85c9269a8f000ecc160920483a29f29daece33ed Mon Sep 17 00:00:00 2001 From: Wang Zichong Date: Fri, 10 Apr 2026 14:46:35 +0800 Subject: [PATCH 1/2] staged change 5s timeout --- .../tray/package/StashedItemPositioner.qml | 22 +++- .../dock/tray/package/TrayItemPositioner.qml | 23 ++++ panels/dock/tray/traysortordermodel.cpp | 116 ++++++++++++++++++ panels/dock/tray/traysortordermodel.h | 15 +++ 4 files changed, 175 insertions(+), 1 deletion(-) diff --git a/panels/dock/tray/package/StashedItemPositioner.qml b/panels/dock/tray/package/StashedItemPositioner.qml index 0179c55ab..cc4b595ed 100644 --- a/panels/dock/tray/package/StashedItemPositioner.qml +++ b/panels/dock/tray/package/StashedItemPositioner.qml @@ -8,7 +8,13 @@ import org.deepin.ds.dock.tray 1.0 as DDT Control { id: root - property bool itemVisible: !DDT.TraySortOrderModel.isUpdating + property bool itemVisible: { + // Startup phase: hide all items without triggering animations + if (DDT.TraySortOrderModel.startupPhase) { + return false + } + return !DDT.TraySortOrderModel.isUpdating + } spacing: 0 padding: 0 @@ -21,6 +27,12 @@ Control { NumberAnimation { duration: 200; easing.type: Easing.OutQuad } } states: [ + State { + name: "startup-hidden" + when: DDT.TraySortOrderModel.startupPhase + PropertyChanges { target: root; opacity: 0.0 } + PropertyChanges { target: root; visible: false } + }, State { when: root.itemVisible PropertyChanges { target: root; opacity: 1.0 } @@ -33,6 +45,14 @@ Control { } ] transitions: [ + Transition { + from: "startup-hidden" + to: "*" + SequentialAnimation { + PropertyAction { target: root; property: "visible"; value: true } + NumberAnimation { property: "opacity"; duration: 300; easing.type: Easing.OutQuad } + } + }, Transition { to: "item-invisible" SequentialAnimation { diff --git a/panels/dock/tray/package/TrayItemPositioner.qml b/panels/dock/tray/package/TrayItemPositioner.qml index 47f935804..1a9fa8870 100644 --- a/panels/dock/tray/package/TrayItemPositioner.qml +++ b/panels/dock/tray/package/TrayItemPositioner.qml @@ -9,6 +9,11 @@ import org.deepin.ds.dock.tray 1.0 as DDT Control { id: root property bool itemVisible: { + // Startup phase: hide all items without triggering animations + if (DDT.TraySortOrderModel.startupPhase) { + return false + } + // Update phase: hide to avoid layout flicker if (DDT.TraySortOrderModel.isUpdating) { return false } @@ -54,6 +59,13 @@ Control { NumberAnimation { duration: 200; easing.type: collapsed || !DDT.TraySortOrderModel.isCollapsing ? Easing.OutQuad : Easing.InQuad } } states: [ + State { + name: "startup-hidden" + when: DDT.TraySortOrderModel.startupPhase + PropertyChanges { target: root; opacity: 0.0 } + PropertyChanges { target: root; scale: 0.8 } + PropertyChanges { target: root; visible: false } + }, State { when: root.itemVisible PropertyChanges { target: root; opacity: 1.0 } @@ -68,6 +80,17 @@ Control { } ] transitions: [ + Transition { + from: "startup-hidden" + to: "*" + SequentialAnimation { + PropertyAction { target: root; property: "visible"; value: true } + ParallelAnimation { + NumberAnimation { property: "opacity"; from: 0.0; to: 1.0; duration: 300; easing.type: Easing.OutQuad } + NumberAnimation { property: "scale"; from: 0.8; to: 1.0; duration: 300; easing.type: Easing.OutBack } + } + } + }, Transition { to: "item-invisible" SequentialAnimation { diff --git a/panels/dock/tray/traysortordermodel.cpp b/panels/dock/tray/traysortordermodel.cpp index 5d2a7b06a..5e527b823 100644 --- a/panels/dock/tray/traysortordermodel.cpp +++ b/panels/dock/tray/traysortordermodel.cpp @@ -23,6 +23,7 @@ const QString SECTION_PINNED = QLatin1String("pinned"); TraySortOrderModel::TraySortOrderModel(QObject *parent) : QStandardItemModel(parent) , m_dconfig(Dtk::Core::DConfig::create("org.deepin.dde.shell", "org.deepin.ds.dock.tray")) + , m_startupPhase(true) { QHash defaultRoleNames = roleNames(); defaultRoleNames.insert({ @@ -63,6 +64,31 @@ TraySortOrderModel::TraySortOrderModel(QObject *parent) qDebug() << "actionsAlwaysVisibleChanged"; updateVisualIndexes(); }); + + // Initialize startup phase management + m_expectedPluginCount = calculateExpectedPluginCount(); + qDebug() << "Expected plugin count from config:" << m_expectedPluginCount; + + // Initialize startup timeout timer (5 seconds) + m_startupTimeoutTimer = new QTimer(this); + m_startupTimeoutTimer->setSingleShot(true); + m_startupTimeoutTimer->setInterval(5000); + connect(m_startupTimeoutTimer, &QTimer::timeout, this, [this](){ + if (m_startupPhase) { + int loadedCount = 0; + for (const QVariantMap &surface : m_availableSurfaces) { + QString surfaceId = surface.value("surfaceId").toString(); + if (!surfaceId.startsWith("internal/")) { + loadedCount++; + } + } + qWarning() << "Startup phase timeout, forcing completion. Expected:" + << m_expectedPluginCount << "Loaded:" << loadedCount; + setStartupPhase(false); + } + }); + m_startupTimeoutTimer->start(); + updateVisualIndexes(); } @@ -577,6 +603,9 @@ void TraySortOrderModel::onAvailableSurfacesChanged() updateVisualIndexes(); // and also save the current sort order saveDataToDConfig(); + + // Check if startup phase should end + checkStartupCompletion(); } void TraySortOrderModel::handlePluginVisibleChanged(const QString &surfaceId, bool visible) @@ -676,4 +705,91 @@ void TraySortOrderModel::clearStagedDrop() updateVisualIndexes(); } +bool TraySortOrderModel::startupPhase() const +{ + return m_startupPhase; +} + +void TraySortOrderModel::setStartupPhase(bool phase) +{ + if (m_startupPhase == phase) + return; + m_startupPhase = phase; + emit startupPhaseChanged(phase); + + if (!phase && m_startupTimeoutTimer) { + m_startupTimeoutTimer->stop(); + } +} + +int TraySortOrderModel::calculateExpectedPluginCount() const +{ + // Calculate the count of all plugins recorded in config + // Note: internal/* items are not counted as they are built-in + QSet allIds; + + for (const QString &id : m_stashedIds) { + if (!id.startsWith("internal/")) { + allIds.insert(id); + } + } + for (const QString &id : m_collapsableIds) { + if (!id.startsWith("internal/")) { + allIds.insert(id); + } + } + for (const QString &id : m_pinnedIds) { + if (!id.startsWith("internal/")) { + allIds.insert(id); + } + } + for (const QString &id : m_fixedIds) { + if (!id.startsWith("internal/")) { + allIds.insert(id); + } + } + + return allIds.size(); +} + +void TraySortOrderModel::checkStartupCompletion() +{ + if (!m_startupPhase) { + return; + } + + // Calculate the count of currently loaded non-internal plugins + int loadedCount = 0; + for (const QVariantMap &surface : m_availableSurfaces) { + QString surfaceId = surface.value("surfaceId").toString(); + if (!surfaceId.startsWith("internal/")) { + loadedCount++; + } + } + + qDebug() << "Startup check - Expected:" << m_expectedPluginCount + << "Loaded:" << loadedCount; + + // If config has no recorded plugins, it's first run, wait for timeout or at least one plugin + if (m_expectedPluginCount == 0) { + // First run, wait for at least one plugin then show + if (loadedCount > 0) { + // Give a short buffer time, there might be more plugins loading + QTimer::singleShot(300, [this](){ + if (m_startupPhase) { + qDebug() << "First run, showing tray items"; + setStartupPhase(false); + } + }); + } + return; + } + + // When loaded count reaches expected, end startup phase + if (loadedCount >= m_expectedPluginCount) { + qDebug() << "All expected plugins loaded, ending startup phase"; + setStartupPhase(false); + } +} + } diff --git a/panels/dock/tray/traysortordermodel.h b/panels/dock/tray/traysortordermodel.h index 69b1a57ef..f3fa27d25 100644 --- a/panels/dock/tray/traysortordermodel.h +++ b/panels/dock/tray/traysortordermodel.h @@ -7,6 +7,7 @@ #include "constants.h" #include #include +#include namespace Dtk { namespace Core { @@ -26,6 +27,7 @@ class TraySortOrderModel : public QStandardItemModel Q_PROPERTY(bool isCollapsing MEMBER m_isCollapsing NOTIFY isCollapsingChanged) Q_PROPERTY(bool actionsAlwaysVisible MEMBER m_actionsAlwaysVisible NOTIFY actionsAlwaysVisibleChanged) Q_PROPERTY(bool isUpdating MEMBER m_isUpdating NOTIFY isUpdatingChanged) + Q_PROPERTY(bool startupPhase READ startupPhase NOTIFY startupPhaseChanged) 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) @@ -75,11 +77,14 @@ class TraySortOrderModel : public QStandardItemModel Q_INVOKABLE void commitStagedDrop(); Q_INVOKABLE void clearStagedDrop(); + bool startupPhase() const; + signals: void collapsedChanged(bool); void isCollapsingChanged(bool); void actionsAlwaysVisibleChanged(bool); void isUpdatingChanged(bool); + void startupPhaseChanged(bool); void visualItemCountChanged(int); void availableSurfacesChanged(const QList &); void stagedDropChanged(); @@ -90,6 +95,7 @@ class TraySortOrderModel : public QStandardItemModel bool m_isCollapsing = false; bool m_actionsAlwaysVisible = false; bool m_isUpdating = false; + bool m_startupPhase = true; std::unique_ptr m_dconfig; // this is for the plugins that currently available. QList m_availableSurfaces; @@ -106,6 +112,10 @@ class TraySortOrderModel : public QStandardItemModel // Staged drop state for drag preview QString m_stagedSurfaceId; int m_stagedVisualIndex = -1; + + // Startup phase management + QTimer *m_startupTimeoutTimer = nullptr; + int m_expectedPluginCount = 0; QStandardItem * findItemByVisualIndex(int visualIndex, VisualSections visualSection) const; QStringList * getSection(const QString & sectionType); @@ -124,6 +134,11 @@ class TraySortOrderModel : public QStandardItemModel void loadDataFromDConfig(); void saveDataToDConfig(); void handlePluginVisibleChanged(const QString &surfaceId, bool visible); + + // Startup phase management + void checkStartupCompletion(); + int calculateExpectedPluginCount() const; + void setStartupPhase(bool phase); private slots: void onAvailableSurfacesChanged(); From 7aa299aa015ece9e3ac6ee0eaed1092d206d33ee Mon Sep 17 00:00:00 2001 From: Wang Zichong Date: Wed, 15 Apr 2026 17:00:49 +0800 Subject: [PATCH 2/2] plan b: 500ms debounce --- panels/dock/tray/traysortordermodel.cpp | 133 +++++------------------- panels/dock/tray/traysortordermodel.h | 16 +-- 2 files changed, 29 insertions(+), 120 deletions(-) diff --git a/panels/dock/tray/traysortordermodel.cpp b/panels/dock/tray/traysortordermodel.cpp index 5e527b823..995275b35 100644 --- a/panels/dock/tray/traysortordermodel.cpp +++ b/panels/dock/tray/traysortordermodel.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -23,7 +24,6 @@ const QString SECTION_PINNED = QLatin1String("pinned"); TraySortOrderModel::TraySortOrderModel(QObject *parent) : QStandardItemModel(parent) , m_dconfig(Dtk::Core::DConfig::create("org.deepin.dde.shell", "org.deepin.ds.dock.tray")) - , m_startupPhase(true) { QHash defaultRoleNames = roleNames(); defaultRoleNames.insert({ @@ -65,29 +65,16 @@ TraySortOrderModel::TraySortOrderModel(QObject *parent) updateVisualIndexes(); }); - // Initialize startup phase management - m_expectedPluginCount = calculateExpectedPluginCount(); - qDebug() << "Expected plugin count from config:" << m_expectedPluginCount; - - // Initialize startup timeout timer (5 seconds) - m_startupTimeoutTimer = new QTimer(this); - m_startupTimeoutTimer->setSingleShot(true); - m_startupTimeoutTimer->setInterval(5000); - connect(m_startupTimeoutTimer, &QTimer::timeout, this, [this](){ + // Startup phase timer: end startup phase after 500ms of no new surfaces + m_startupTimer = new QTimer(this); + m_startupTimer->setSingleShot(true); + m_startupTimer->setInterval(500); + connect(m_startupTimer, &QTimer::timeout, this, [this](){ if (m_startupPhase) { - int loadedCount = 0; - for (const QVariantMap &surface : m_availableSurfaces) { - QString surfaceId = surface.value("surfaceId").toString(); - if (!surfaceId.startsWith("internal/")) { - loadedCount++; - } - } - qWarning() << "Startup phase timeout, forcing completion. Expected:" - << m_expectedPluginCount << "Loaded:" << loadedCount; + qDebug() << "Startup phase ended, showing all tray items"; setStartupPhase(false); } }); - m_startupTimeoutTimer->start(); updateVisualIndexes(); } @@ -604,8 +591,23 @@ void TraySortOrderModel::onAvailableSurfacesChanged() // and also save the current sort order saveDataToDConfig(); - // Check if startup phase should end - checkStartupCompletion(); + // During startup phase, reset timer on each new surface to batch updates + if (m_startupPhase && m_startupTimer) { + m_startupTimer->start(); + } +} + +bool TraySortOrderModel::startupPhase() const +{ + return m_startupPhase; +} + +void TraySortOrderModel::setStartupPhase(bool phase) +{ + if (m_startupPhase == phase) + return; + m_startupPhase = phase; + emit startupPhaseChanged(phase); } void TraySortOrderModel::handlePluginVisibleChanged(const QString &surfaceId, bool visible) @@ -705,91 +707,4 @@ void TraySortOrderModel::clearStagedDrop() updateVisualIndexes(); } -bool TraySortOrderModel::startupPhase() const -{ - return m_startupPhase; -} - -void TraySortOrderModel::setStartupPhase(bool phase) -{ - if (m_startupPhase == phase) - return; - m_startupPhase = phase; - emit startupPhaseChanged(phase); - - if (!phase && m_startupTimeoutTimer) { - m_startupTimeoutTimer->stop(); - } -} - -int TraySortOrderModel::calculateExpectedPluginCount() const -{ - // Calculate the count of all plugins recorded in config - // Note: internal/* items are not counted as they are built-in - QSet allIds; - - for (const QString &id : m_stashedIds) { - if (!id.startsWith("internal/")) { - allIds.insert(id); - } - } - for (const QString &id : m_collapsableIds) { - if (!id.startsWith("internal/")) { - allIds.insert(id); - } - } - for (const QString &id : m_pinnedIds) { - if (!id.startsWith("internal/")) { - allIds.insert(id); - } - } - for (const QString &id : m_fixedIds) { - if (!id.startsWith("internal/")) { - allIds.insert(id); - } - } - - return allIds.size(); -} - -void TraySortOrderModel::checkStartupCompletion() -{ - if (!m_startupPhase) { - return; - } - - // Calculate the count of currently loaded non-internal plugins - int loadedCount = 0; - for (const QVariantMap &surface : m_availableSurfaces) { - QString surfaceId = surface.value("surfaceId").toString(); - if (!surfaceId.startsWith("internal/")) { - loadedCount++; - } - } - - qDebug() << "Startup check - Expected:" << m_expectedPluginCount - << "Loaded:" << loadedCount; - - // If config has no recorded plugins, it's first run, wait for timeout or at least one plugin - if (m_expectedPluginCount == 0) { - // First run, wait for at least one plugin then show - if (loadedCount > 0) { - // Give a short buffer time, there might be more plugins loading - QTimer::singleShot(300, [this](){ - if (m_startupPhase) { - qDebug() << "First run, showing tray items"; - setStartupPhase(false); - } - }); - } - return; - } - - // When loaded count reaches expected, end startup phase - if (loadedCount >= m_expectedPluginCount) { - qDebug() << "All expected plugins loaded, ending startup phase"; - setStartupPhase(false); - } -} - } diff --git a/panels/dock/tray/traysortordermodel.h b/panels/dock/tray/traysortordermodel.h index f3fa27d25..21ae1d7c0 100644 --- a/panels/dock/tray/traysortordermodel.h +++ b/panels/dock/tray/traysortordermodel.h @@ -27,7 +27,7 @@ class TraySortOrderModel : public QStandardItemModel Q_PROPERTY(bool isCollapsing MEMBER m_isCollapsing NOTIFY isCollapsingChanged) Q_PROPERTY(bool actionsAlwaysVisible MEMBER m_actionsAlwaysVisible NOTIFY actionsAlwaysVisibleChanged) Q_PROPERTY(bool isUpdating MEMBER m_isUpdating NOTIFY isUpdatingChanged) - Q_PROPERTY(bool startupPhase READ startupPhase NOTIFY startupPhaseChanged) + Q_PROPERTY(bool startupPhase READ startupPhase WRITE setStartupPhase NOTIFY startupPhaseChanged) 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) @@ -76,8 +76,10 @@ class TraySortOrderModel : public QStandardItemModel Q_INVOKABLE void stageDropPosition(const QString &surfaceId, int visualIndex); Q_INVOKABLE void commitStagedDrop(); Q_INVOKABLE void clearStagedDrop(); - + + // Startup phase control bool startupPhase() const; + Q_INVOKABLE void setStartupPhase(bool phase); signals: void collapsedChanged(bool); @@ -97,6 +99,7 @@ class TraySortOrderModel : public QStandardItemModel bool m_isUpdating = false; bool m_startupPhase = true; std::unique_ptr m_dconfig; + QTimer *m_startupTimer = nullptr; // this is for the plugins that currently available. QList m_availableSurfaces; // these are the sort order data source, it might contain items that are no longer existed. @@ -112,10 +115,6 @@ class TraySortOrderModel : public QStandardItemModel // Staged drop state for drag preview QString m_stagedSurfaceId; int m_stagedVisualIndex = -1; - - // Startup phase management - QTimer *m_startupTimeoutTimer = nullptr; - int m_expectedPluginCount = 0; QStandardItem * findItemByVisualIndex(int visualIndex, VisualSections visualSection) const; QStringList * getSection(const QString & sectionType); @@ -134,11 +133,6 @@ class TraySortOrderModel : public QStandardItemModel void loadDataFromDConfig(); void saveDataToDConfig(); void handlePluginVisibleChanged(const QString &surfaceId, bool visible); - - // Startup phase management - void checkStartupCompletion(); - int calculateExpectedPluginCount() const; - void setStartupPhase(bool phase); private slots: void onAvailableSurfacesChanged();