diff --git a/applets/dde-apps/CMakeLists.txt b/applets/dde-apps/CMakeLists.txt index bf67ebc8a..56fdac274 100644 --- a/applets/dde-apps/CMakeLists.txt +++ b/applets/dde-apps/CMakeLists.txt @@ -4,6 +4,7 @@ find_package(Qt${QT_VERSION_MAJOR} ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS DBus) find_package(DDEApplicationManager REQUIRED) +find_package(GLIB2 REQUIRED COMPONENTS GIO) find_package(yaml-cpp REQUIRED) set_source_files_properties( @@ -55,10 +56,14 @@ add_library(dde-apps SHARED ${DBUS_INTERFACES} categoryutils.h itemspage.cpp itemspage.h + trashmonitor.cpp + trashmonitor.h ) target_link_libraries(dde-apps PRIVATE dde-shell-frame + Qt${QT_VERSION_MAJOR}::Concurrent + GLIB2::GIO yaml-cpp ) diff --git a/applets/dde-apps/amappitemmodel.cpp b/applets/dde-apps/amappitemmodel.cpp index 5801dea82..a62f44086 100644 --- a/applets/dde-apps/amappitemmodel.cpp +++ b/applets/dde-apps/amappitemmodel.cpp @@ -6,6 +6,7 @@ #include "amappitem.h" #include "appitemmodel.h" #include "objectmanager1interface.h" +#include "trashmonitor.h" #include @@ -16,6 +17,7 @@ namespace apps AMAppItemModel::AMAppItemModel(QObject *parent) : AppItemModel(parent) , m_manager(new ObjectManager("org.desktopspec.ApplicationManager1", "/org/desktopspec/ApplicationManager1", QDBusConnection::sessionBus(), this)) + , m_trashMonitor(new TrashMonitor(this)) , m_ready(false) { qRegisterMetaType(); @@ -35,6 +37,18 @@ AMAppItemModel::AMAppItemModel(QObject *parent) return; } appendRow(new AMAppItem(objPath, interfacesAndProperties)); + updateTrashIcon(); + }); + + connect(m_trashMonitor, &TrashMonitor::emptyChanged, this, &AMAppItemModel::updateTrashIcon); + connect(this, &QAbstractItemModel::dataChanged, this, + [this](const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList &roles) { + const auto trashIndex = match(index(0, 0), AppItemModel::DesktopIdRole, + QStringLiteral("dde-trash"), 1, Qt::MatchExactly).value(0); + if (trashIndex.isValid() && trashIndex.row() >= topLeft.row() && trashIndex.row() <= bottomRight.row() + && (roles.isEmpty() || roles.contains(AppItemModel::IconNameRole))) { + updateTrashIcon(); + } }); connect(m_manager, &ObjectManager::InterfacesRemoved, this, [this](const QDBusObjectPath &objPath, const QStringList &interfaces) { @@ -69,6 +83,9 @@ AMAppItemModel::AMAppItemModel(QObject *parent) appendRow(new AMAppItem(path, app.value())); } + + updateTrashIcon(); + m_ready = true; Q_EMIT readyChanged(true); qCDebug(appsLog) << "AMAppItemModel is now ready with apps counts:" << rowCount(); @@ -90,4 +107,17 @@ AMAppItem * AMAppItemModel::appItem(const QString &id) return nullptr; } +void AMAppItemModel::updateTrashIcon() +{ + auto *trash = appItem(QStringLiteral("dde-trash")); + if (!trash) + return; + + const QString iconName = m_trashMonitor->isEmpty() + ? QStringLiteral("user-trash") + : QStringLiteral("user-trash-full"); + if (trash->appIconName() != iconName) + trash->setAppIconName(iconName); +} + } diff --git a/applets/dde-apps/amappitemmodel.h b/applets/dde-apps/amappitemmodel.h index 5ace8b3c5..9c182c42f 100644 --- a/applets/dde-apps/amappitemmodel.h +++ b/applets/dde-apps/amappitemmodel.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 @@ -10,6 +10,7 @@ namespace apps { class AMAppItem; +class TrashMonitor; class AMAppItemModel : public AppItemModel { Q_OBJECT @@ -25,7 +26,10 @@ class AMAppItemModel : public AppItemModel void readyChanged(bool); private: + void updateTrashIcon(); + bool m_ready; ObjectManager *m_manager; + TrashMonitor *m_trashMonitor; }; } \ No newline at end of file diff --git a/applets/dde-apps/trashmonitor.cpp b/applets/dde-apps/trashmonitor.cpp new file mode 100644 index 000000000..3dd549511 --- /dev/null +++ b/applets/dde-apps/trashmonitor.cpp @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "trashmonitor.h" + +#include + +Q_LOGGING_CATEGORY(trashMonitorLog, "org.deepin.dde.shell.dde-apps.trash") + +namespace apps +{ +TrashMonitor::TrashMonitor(QObject *parent) + : QObject(parent) + , m_trash(g_file_new_for_uri("trash:///")) +{ + updateState(); + + GError *error = nullptr; + m_monitor = g_file_monitor_file(m_trash, G_FILE_MONITOR_NONE, nullptr, &error); + if (!m_monitor) { + qCWarning(trashMonitorLog) << "Failed to monitor trash:" + << (error ? error->message : "unknown error"); + g_clear_error(&error); + return; + } + + g_signal_connect(m_monitor, "changed", G_CALLBACK(onTrashChanged), this); +} + +TrashMonitor::~TrashMonitor() +{ + if (m_monitor) + g_object_unref(m_monitor); + if (m_trash) + g_object_unref(m_trash); +} + +bool TrashMonitor::isEmpty() const +{ + return m_empty; +} + +void TrashMonitor::updateState() +{ + GError *error = nullptr; + GFileInfo *info = g_file_query_info(m_trash, + G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT, + G_FILE_QUERY_INFO_NONE, + nullptr, + &error); + if (!info) { + qCWarning(trashMonitorLog) << "Failed to query trash item count:" + << (error ? error->message : "unknown error"); + g_clear_error(&error); + return; + } + + const bool empty = g_file_info_get_attribute_uint32(info, G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT) == 0; + g_object_unref(info); + + if (m_empty == empty) + return; + + m_empty = empty; + Q_EMIT emptyChanged(m_empty); +} + +void TrashMonitor::onTrashChanged(GFileMonitor *monitor, + GFile *file, + GFile *otherFile, + GFileMonitorEvent eventType, + gpointer userData) +{ + Q_UNUSED(monitor) + Q_UNUSED(file) + Q_UNUSED(otherFile) + + switch (eventType) { + case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED: + case G_FILE_MONITOR_EVENT_CHANGED: + case G_FILE_MONITOR_EVENT_CREATED: + case G_FILE_MONITOR_EVENT_DELETED: + static_cast(userData)->updateState(); + break; + default: + break; + } +} +} diff --git a/applets/dde-apps/trashmonitor.h b/applets/dde-apps/trashmonitor.h new file mode 100644 index 000000000..defc491f9 --- /dev/null +++ b/applets/dde-apps/trashmonitor.h @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#undef signals +#include +#define signals Q_SIGNALS + +namespace apps +{ +class TrashMonitor : public QObject +{ + Q_OBJECT + +public: + explicit TrashMonitor(QObject *parent = nullptr); + ~TrashMonitor() override; + + bool isEmpty() const; + +signals: + void emptyChanged(bool empty); + +private: + void updateState(); + static void onTrashChanged(GFileMonitor *monitor, + GFile *file, + GFile *otherFile, + GFileMonitorEvent eventType, + gpointer userData); + + GFile *m_trash = nullptr; + GFileMonitor *m_monitor = nullptr; + bool m_empty = true; +}; +}