From f3e9858b88f5c50d26af54f4e516ec7057b23b6c Mon Sep 17 00:00:00 2001 From: hewenfei Date: Tue, 9 Jan 2024 09:56:50 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=9C=80=E8=BF=91?= =?UTF-8?q?=E5=AE=89=E8=A3=85=E5=92=8C=E5=BA=94=E7=94=A8=E5=88=86=E7=B1=BB?= =?UTF-8?q?model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/data-entity.cpp | 3 + src/data-entity.h | 6 +- src/extension/menu-extension-plugin.h | 1 + src/libappdata/app-category-model.cpp | 155 ++++++++++++ src/libappdata/app-category-model.h | 63 +++++ src/libappdata/app-database-interface.cpp | 34 ++- src/libappdata/app-database-interface.h | 22 +- src/libappdata/basic-app-model.cpp | 30 ++- src/libappdata/basic-app-model.h | 27 ++- src/libappdata/combined-list-model.cpp | 255 ++++++++++++++++++++ src/libappdata/combined-list-model.h | 79 ++++++ src/libappdata/recently-installed-model.cpp | 95 ++++++++ src/libappdata/recently-installed-model.h | 51 ++++ 13 files changed, 801 insertions(+), 20 deletions(-) create mode 100644 src/libappdata/app-category-model.cpp create mode 100644 src/libappdata/app-category-model.h create mode 100644 src/libappdata/combined-list-model.cpp create mode 100644 src/libappdata/combined-list-model.h create mode 100644 src/libappdata/recently-installed-model.cpp create mode 100644 src/libappdata/recently-installed-model.h diff --git a/src/data-entity.cpp b/src/data-entity.cpp index ac33904..31cba1d 100644 --- a/src/data-entity.cpp +++ b/src/data-entity.cpp @@ -233,6 +233,7 @@ QHash DataEntity::AppRoleNames() names.insert(DataEntity::Favorite, "favorite"); names.insert(DataEntity::Top, "top"); names.insert(DataEntity::RecentInstall, "recentInstall"); + names.insert(DataEntity::Entity, "entity"); return names; } @@ -303,6 +304,8 @@ QVariant DataEntity::getValue(DataEntity::PropertyName property) const return d->top; case RecentInstall: return d->recentInstall; + case Entity: + return QVariant::fromValue(*this); default: break; } diff --git a/src/data-entity.h b/src/data-entity.h index 9793106..e82d915 100644 --- a/src/data-entity.h +++ b/src/data-entity.h @@ -61,13 +61,14 @@ public: Category, /**> 应用类别 */ Group, /**> 应用分组 */ FirstLetter, /**> 应用名称首字母 */ - InstallationTime, /**> 安装时间 */ + InstallationTime, /**> 安装时间 format: yyyy-MM-dd hh:mm:ss */ IsLaunched, /**> 是否启动过该程序 */ LaunchTimes, /**> 应用被启动的次数 */ IsLocked, /**> 应用是否锁定 */ Favorite, /**> 是否被收藏及序号, 小于或等于0表示未被收藏 */ Top, /**> 是否被置顶及序号, 小于或等于0表示未被置顶 */ - RecentInstall + RecentInstall, + Entity /**> 返回自己的拷贝 */ }; DataEntity(); DataEntity(DataType::Type type, const QString& name, const QString& icon, const QString& comment, const QString& extraData); @@ -140,5 +141,6 @@ private: } // UkuiMenu Q_DECLARE_METATYPE(UkuiMenu::DataType) +Q_DECLARE_METATYPE(UkuiMenu::DataEntity) #endif //UKUI_MENU_DATA_ENTITY_H diff --git a/src/extension/menu-extension-plugin.h b/src/extension/menu-extension-plugin.h index 63cc9a5..03a84a1 100644 --- a/src/extension/menu-extension-plugin.h +++ b/src/extension/menu-extension-plugin.h @@ -32,6 +32,7 @@ class ContextMenuExtension; class Q_DECL_EXPORT MenuExtensionPlugin : public QObject { + Q_OBJECT public: explicit MenuExtensionPlugin(QObject *parent = nullptr); ~MenuExtensionPlugin() override; diff --git a/src/libappdata/app-category-model.cpp b/src/libappdata/app-category-model.cpp new file mode 100644 index 0000000..85b936d --- /dev/null +++ b/src/libappdata/app-category-model.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2024, KylinSoft Co., Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: hxf + * + */ + +#include "app-category-model.h" +#include "basic-app-model.h" + +#include + +namespace UkuiMenu { + +AppCategoryModel::AppCategoryModel(QObject *parent) : QSortFilterProxyModel(parent) +{ + m_mainCategories.insert(QStringLiteral("Audio"), {tr("Audio"), 0}); + m_mainCategories.insert(QStringLiteral("AudioVideo"), {tr("AudioVideo"), 1}); + m_mainCategories.insert(QStringLiteral("Development"), {tr("Development"), 2}); + m_mainCategories.insert(QStringLiteral("Education"), {tr("Education"), 3}); + m_mainCategories.insert(QStringLiteral("Game"), {tr("Game"), 4}); + m_mainCategories.insert(QStringLiteral("Graphics"), {tr("Graphics"), 5}); + m_mainCategories.insert(QStringLiteral("Network"), {tr("Network"), 6}); + m_mainCategories.insert(QStringLiteral("Office"), {tr("Office"), 7}); + m_mainCategories.insert(QStringLiteral("Science"), {tr("Science"), 8}); + m_mainCategories.insert(QStringLiteral("Settings"), {tr("Settings"), 9}); + m_mainCategories.insert(QStringLiteral("System"), {tr("System"), 10}); + m_mainCategories.insert(QStringLiteral("Utility"), {tr("Utility"), 11}); + m_mainCategories.insert(QStringLiteral("Video"), {tr("Video"), 12}); + m_mainCategories.insert(QStringLiteral("Other"), {tr("Other"), 13}); + + QSortFilterProxyModel::setSourceModel(BasicAppModel::instance()); + QSortFilterProxyModel::sort(0); +} + +AppCategoryModel::Mode AppCategoryModel::mode() const +{ + return m_mode; +} + +void AppCategoryModel::setMode(AppCategoryModel::Mode mode) +{ + if (m_mode == mode) { + return; + } + + m_mode = mode; + // TODO: 刷新 + invalidate(); +} + +QVariant AppCategoryModel::data(const QModelIndex &index, int role) const +{ + if (!checkIndex(index, CheckIndexOption::IndexIsValid)) { + return {}; + } + + if (role == DataEntity::Group) { + if (m_mode == FirstLatter) { + return AppCategoryModel::getFirstLatterUpper(QSortFilterProxyModel::data(index, DataEntity::FirstLetter).toString()); + } else { + return getCategoryName(QSortFilterProxyModel::data(index, DataEntity::Category).toString()); + } + } + + return QSortFilterProxyModel::data(index, role); +} + +bool AppCategoryModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const +{ + return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); +} + +bool AppCategoryModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const +{ + int result = 0; + if (m_mode == FirstLatter) { + QString leftKey = AppCategoryModel::getFirstLatterUpper(source_left.data(DataEntity::FirstLetter).toString()); + QString rightKey = AppCategoryModel::getFirstLatterUpper(source_right.data(DataEntity::FirstLetter).toString()); + + result = leftKey.compare(rightKey); + + } else { + // 按分类进行排序 + int leftIndex = getCategoryIndex(source_left.data(DataEntity::Category).toString()); + int rightIndex = getCategoryIndex(source_right.data(DataEntity::Category).toString()); + + result = (leftIndex < rightIndex) ? -1 : (leftIndex > rightIndex) ? 1 : 0; + } + + if (result == 0) { + // 分类相同时,按打开次数排序; model使用的升序排序,为了保持使用次数多的在前,所以此处使用 > 比较符, + return source_left.data(DataEntity::LaunchTimes).toInt() > source_right.data(DataEntity::LaunchTimes).toInt(); + } + + return result < 0; +} + +inline QString AppCategoryModel::getCategoryName(const QString &categories) const +{ + return m_mainCategories[getCategoryKey(categories)].first; +} + +inline int AppCategoryModel::getCategoryIndex(const QString &categories) const +{ + return m_mainCategories[getCategoryKey(categories)].second; +} + +QString AppCategoryModel::getCategoryKey(const QString &categories) const +{ + QStringList list; + // 某些应用使用空格(" ")进行分隔,此处做一下处理 + if (categories.contains( QStringLiteral(";"))) { + list = categories.split(QStringLiteral(";")); + } else { + list = categories.split(QStringLiteral(" ")); + } + + for (const auto &key : list) { + if (m_mainCategories.contains(key)) { + return key; + } + } + + return QStringLiteral("Other"); +} + +QString AppCategoryModel::getFirstLatterUpper(const QString &pinyinName) +{ + if (!pinyinName.isEmpty()) { + QChar first = pinyinName.at(0).toUpper(); + if (first >= 'A' && first <= 'Z') { + return first; + } else if (first >= '0' && first <= '9') { + return QStringLiteral("#"); + } + } + + return QStringLiteral("&"); +} + +} // UkuiMenu diff --git a/src/libappdata/app-category-model.h b/src/libappdata/app-category-model.h new file mode 100644 index 0000000..1f156b4 --- /dev/null +++ b/src/libappdata/app-category-model.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2024, KylinSoft Co., Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: hxf + * + */ + +#ifndef UKUI_MENU_APP_CATEGORY_MODEL_H +#define UKUI_MENU_APP_CATEGORY_MODEL_H + +#include "app-list-plugin.h" +#include +#include + +namespace UkuiMenu { + +class AppCategoryModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + enum Mode { + FirstLatter, /**> 按首字母排序 */ + Category /**> 按类型排序 */ + }; + Q_ENUM(Mode) + + explicit AppCategoryModel(QObject *parent = nullptr); + QVariant data(const QModelIndex &index, int role) const override; + + Mode mode() const; + void setMode(Mode mode); + +protected: + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; + bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override; + +private: + static QString getFirstLatterUpper(const QString &pinyinName) ; + inline QString getCategoryName(const QString &categories) const; + inline int getCategoryIndex(const QString &categories) const; + QString getCategoryKey(const QString &categories) const; + +private: + Mode m_mode { Category }; + QMap > m_mainCategories; +}; + +} // UkuiMenu + +#endif //UKUI_MENU_APP_CATEGORY_MODEL_H diff --git a/src/libappdata/app-database-interface.cpp b/src/libappdata/app-database-interface.cpp index 5951e03..c8bc35a 100644 --- a/src/libappdata/app-database-interface.cpp +++ b/src/libappdata/app-database-interface.cpp @@ -1,6 +1,22 @@ -// -// Created by hxf on 23-12-11. -// +/* + * Copyright (C) 2024, KylinSoft Co., Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: hxf + * + */ #include "app-database-interface.h" @@ -46,6 +62,7 @@ private: UkuiSearch::ApplicationInfo *appDatabase {nullptr}; UkuiSearch::ApplicationProperties properties; + // 设置我们需要的属性和值 UkuiSearch::ApplicationPropertyMap filter; }; @@ -80,7 +97,7 @@ AppDatabaseWorkerPrivate::AppDatabaseWorkerPrivate(AppDatabaseInterface *parent) << UkuiSearch::ApplicationProperty::Property::InsertTime << UkuiSearch::ApplicationProperty::Property::Launched; - // 需要从数据库过滤的属性 + // 需要从数据库过滤的属性,满足该条件的数据才会被查询出来 filter.insert(UkuiSearch::ApplicationProperty::Property::DontDisplay, 0); filter.insert(UkuiSearch::ApplicationProperty::Property::AutoStart, 0); @@ -113,11 +130,16 @@ bool AppDatabaseWorkerPrivate::isFiltered(const UkuiSearch::ApplicationPropertyM while (iterator.hasNext()) { iterator.next(); if (appInfo.value(iterator.key()) == iterator.value()) { - return true; + return false; } + + // TODO: 根据数据的类型进行比较 +// bool equals = false; +// QVariant value = appInfo.value(iterator.key()); +// value.userType(); } - return false; + return true; } DataEntityVector AppDatabaseWorkerPrivate::getAllApps() diff --git a/src/libappdata/app-database-interface.h b/src/libappdata/app-database-interface.h index efcf745..c43cf0a 100644 --- a/src/libappdata/app-database-interface.h +++ b/src/libappdata/app-database-interface.h @@ -1,6 +1,22 @@ -// -// Created by hxf on 23-12-11. -// +/* + * Copyright (C) 2024, KylinSoft Co., Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: hxf + * + */ #ifndef UKUI_MENU_APP_DATABASE_INTERFACE_H #define UKUI_MENU_APP_DATABASE_INTERFACE_H diff --git a/src/libappdata/basic-app-model.cpp b/src/libappdata/basic-app-model.cpp index 796c3d4..38cf2fa 100644 --- a/src/libappdata/basic-app-model.cpp +++ b/src/libappdata/basic-app-model.cpp @@ -1,6 +1,22 @@ -// -// Created by hxf on 23-12-11. -// +/* + * Copyright (C) 2024, KylinSoft Co., Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: hxf + * + */ #include "basic-app-model.h" @@ -8,6 +24,12 @@ namespace UkuiMenu { +BasicAppModel *BasicAppModel::instance() +{ + static BasicAppModel model; + return &model; +} + BasicAppModel::BasicAppModel(QObject *parent) : QAbstractListModel(parent) , m_databaseInterface(new AppDatabaseInterface(this)) { @@ -90,7 +112,7 @@ QHash BasicAppModel::roleNames() const return DataEntity::AppRoleNames(); } -AppDatabaseInterface *BasicAppModel::databaseInterface() const +const AppDatabaseInterface *BasicAppModel::databaseInterface() const { return m_databaseInterface; } diff --git a/src/libappdata/basic-app-model.h b/src/libappdata/basic-app-model.h index 5ff93cd..50f617f 100644 --- a/src/libappdata/basic-app-model.h +++ b/src/libappdata/basic-app-model.h @@ -1,6 +1,22 @@ -// -// Created by hxf on 23-12-11. -// +/* + * Copyright (C) 2024, KylinSoft Co., Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: hxf + * + */ #ifndef UKUI_MENU_BASIC_APP_MODEL_H #define UKUI_MENU_BASIC_APP_MODEL_H @@ -17,13 +33,13 @@ class BasicAppModel : public QAbstractListModel { Q_OBJECT public: - explicit BasicAppModel(QObject *parent = nullptr); + static BasicAppModel *instance(); /** * 数据库访问接口 * @return @AppDatabaseInterface * */ - AppDatabaseInterface *databaseInterface() const; + const AppDatabaseInterface *databaseInterface() const; DataEntity appOfIndex(int row) const; int rowCount(const QModelIndex &parent) const override; @@ -38,6 +54,7 @@ private Q_SLOTS: void onAppDeleted(const QStringList &apps); private: + explicit BasicAppModel(QObject *parent = nullptr); int indexOfApp(const QString &appid) const; AppDatabaseInterface *m_databaseInterface {nullptr}; diff --git a/src/libappdata/combined-list-model.cpp b/src/libappdata/combined-list-model.cpp new file mode 100644 index 0000000..c0b6e39 --- /dev/null +++ b/src/libappdata/combined-list-model.cpp @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2024, KylinSoft Co., Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: hxf + * + */ + +#include "combined-list-model.h" + +#include + +namespace UkuiMenu { + +// CombinedListModel +CombinedListModel::CombinedListModel(QObject *parent) : QAbstractItemModel(parent) +{ + +} + +QModelIndex CombinedListModel::index(int row, int column, const QModelIndex &parent) const +{ + if (column != 0 || row < 0 || row >= rowCount(QModelIndex())) { + return {}; + } + + int start = 0; + for (const auto &pair : m_subModels) { + int rc = pair.second; + if (row < (start + rc)) { + return createIndex(row, 0, pair.first); + } else { + start += rc; + } + } + + return {}; +} + +QModelIndex CombinedListModel::parent(const QModelIndex &child) const +{ + return {}; +} + +int CombinedListModel::rowCount(const QModelIndex &parent) const +{ + int count = 0; + for (const auto &pair : m_subModels) { + count += pair.second; + } + + return count; +} + +int CombinedListModel::columnCount(const QModelIndex &parent) const +{ + return 1; +} + +QHash CombinedListModel::roleNames() const +{ + return QAbstractItemModel::roleNames(); +} + +QModelIndex CombinedListModel::mapToSource(const QModelIndex &proxyIndex) const +{ + if (!proxyIndex.isValid()) { + return {}; + } + + auto sourceModel = static_cast(proxyIndex.internalPointer()); + if (!sourceModel) { + return {}; + } + + int offset = offsetOfSubModel(sourceModel); + if (offset < 0) { + return {}; + } + + return sourceModel->index(proxyIndex.row() - offset, 0); +} + +QModelIndex CombinedListModel::mapFromSource(const QModelIndex &sourceIndex) const +{ + if (!sourceIndex.isValid()) { + return {}; + } + + int offset = offsetOfSubModel(sourceIndex.model()); + if (offset < 0) { + return {}; + } + + return createIndex(offset + sourceIndex.row(), 0); +} + +int CombinedListModel::offsetOfSubModel(const QAbstractItemModel *subModel) const +{ + int offset = 0; + for (const auto &pair : m_subModels) { + if (pair.first == subModel) { + return offset; + } else { + offset += pair.second; + } + } + + return -1; +} + +QVariant CombinedListModel::data(const QModelIndex &proxyIndex, int role) const +{ + if (!checkIndex(proxyIndex, CheckIndexOption::IndexIsValid)) { + return {}; + } + + QModelIndex sourceIndex = mapToSource(proxyIndex); + if (!sourceIndex.isValid()) { + return {}; + } + + return sourceIndex.data(role); +} + +int CombinedListModel::subModelCount() const +{ + return m_subModels.size(); +} + +const QAbstractItemModel *CombinedListModel::subModelAt(int index) const +{ + if (index < 0 || index >= m_subModels.size()) { + return nullptr; + } + + return m_subModels.at(index).first; +} + +void CombinedListModel::insertSubModel(QAbstractItemModel *subModel, int index) +{ + if (!subModel || offsetOfSubModel(subModel) >= 0) { + return; + } + + beginResetModel(); + + if (index < 0 || index > m_subModels.size()) { + m_subModels.append({subModel, subModel->rowCount()}); + } else { + m_subModels.insert(index, {subModel, subModel->rowCount()}); + } + + connect(subModel, &QAbstractItemModel::rowsRemoved, + this, [subModel, this] (const QModelIndex &parent, int first, int last) { + int offset = offsetOfSubModel(subModel); + if (offset >= 0) { + beginRemoveRows(mapFromSource(parent), offset + first, offset + last); + for (auto &pair : m_subModels) { + if (pair.first == subModel) { +// pair.second = subModel->rowCount(); + pair.second = pair.second - (last - first + 1); + } + } + endRemoveRows(); + } + }); + + connect(subModel, &QAbstractItemModel::rowsInserted, + this, [subModel, this] (const QModelIndex &parent, int first, int last) { + int offset = offsetOfSubModel(subModel); + if (offset >= 0) { + beginInsertRows(mapFromSource(parent), offset + first, offset + last); + for (auto &pair : m_subModels) { + if (pair.first == subModel) { +// pair.second = subModel->rowCount(); + pair.second = pair.second + (last - first + 1); + } + } + endInsertRows(); + } + }); + + connect(subModel, &QAbstractItemModel::dataChanged, + this, [subModel, this] (const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) { + Q_EMIT dataChanged( mapFromSource(topLeft), mapFromSource(bottomRight), roles); + }); + + connect(subModel, &QAbstractItemModel::modelReset, this, [subModel, this] { + beginResetModel(); + for (auto &pair : m_subModels) { + if (pair.first == subModel) { + pair.second = pair.second; + } + } + endResetModel(); + }); + + connect(subModel, &QAbstractItemModel::layoutAboutToBeChanged, this, [this] { + emit layoutAboutToBeChanged(); + }); + + connect(subModel, &QAbstractItemModel::layoutChanged, this, [this] { + emit layoutChanged(); + }); + + endResetModel(); +} + +void CombinedListModel::removeSubModel(int index) +{ + if (index < 0 || index >= m_subModels.size()) { + return; + } + + QPair pair = m_subModels[index]; + int offset = offsetOfSubModel(pair.first); + + beginRemoveRows(QModelIndex(), offset, offset + pair.second); + pair = m_subModels.takeAt(index); + disconnect(pair.first, nullptr, this, nullptr); + endRemoveRows(); +} + +void CombinedListModel::removeSubModel(QAbstractItemModel *subModel) +{ + removeSubModel(indexOfSubModel(subModel)); +} + +int CombinedListModel::indexOfSubModel(QAbstractItemModel *subModel) +{ + int index = -1; + for (int i = 0; i < m_subModels.size(); ++i) { + if (m_subModels.at(i).first == subModel) { + index = i; + break; + } + } + + return index; +} + +} // UkuiMenu diff --git a/src/libappdata/combined-list-model.h b/src/libappdata/combined-list-model.h new file mode 100644 index 0000000..eacdf47 --- /dev/null +++ b/src/libappdata/combined-list-model.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2024, KylinSoft Co., Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: hxf + * + */ + +#ifndef UKUI_MENU_COMBINED_LIST_MODEL_H +#define UKUI_MENU_COMBINED_LIST_MODEL_H + +#include +#include +#include + +namespace UkuiMenu { + +/** + * 由多个子model组合成的model + * + * 通过index设置或获取子model + * CombinedListModel + */ +class CombinedListModel : public QAbstractItemModel +{ + Q_OBJECT +public: + explicit CombinedListModel(QObject *parent = nullptr); + + QModelIndex index(int row, int column, const QModelIndex &parent) const override; + QModelIndex parent(const QModelIndex &child) const override; + + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + + QModelIndex mapToSource(const QModelIndex &proxyIndex) const; + QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; + + QHash roleNames() const override; + QVariant data(const QModelIndex &proxyIndex, int role) const override; + + // 获取subModel的数量 + int subModelCount() const; + + // 获取index位置和对应的subModel + const QAbstractItemModel *subModelAt(int index) const; + int indexOfSubModel(QAbstractItemModel *subModel); + + /** + * 插入一个subModel + * @param subModel + * @param index 插入的位置,默认放到最后 + */ + void insertSubModel(QAbstractItemModel *subModel, int index = -1); + void removeSubModel(int index); + void removeSubModel(QAbstractItemModel *subModel); + +private: + int offsetOfSubModel(const QAbstractItemModel *subModel) const; + +private: + QVector > m_subModels; +}; + +} // UkuiMenu + +#endif //UKUI_MENU_COMBINED_LIST_MODEL_H diff --git a/src/libappdata/recently-installed-model.cpp b/src/libappdata/recently-installed-model.cpp new file mode 100644 index 0000000..cf5cb96 --- /dev/null +++ b/src/libappdata/recently-installed-model.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2024, KylinSoft Co., Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: hxf + * + */ + +#include "recently-installed-model.h" +#include "basic-app-model.h" + +#include +#include +#include +#include + +namespace UkuiMenu { + +// ====== RecentlyInstalledModel ====== // +RecentlyInstalledModel::RecentlyInstalledModel(QObject *parent) : QSortFilterProxyModel(parent), m_timer(new QTimer(this)) +{ + QSortFilterProxyModel::setSourceModel(BasicAppModel::instance()); + // 触发排序动作 +// QSortFilterProxyModel::sort(0, Qt::DescendingOrder); + QSortFilterProxyModel::sort(0); + + // 每48小时主动刷新 + m_timer->setInterval(48 * 3600000); + m_timer->start(); +} + +bool RecentlyInstalledModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const +{ + QModelIndex sourceIndex = sourceModel()->index(source_row, 0, source_parent); + QDateTime installDate = QDateTime::fromString(sourceIndex.data(DataEntity::InstallationTime).value(), "yyyy-MM-dd hh:mm:ss"); + if (!installDate.isValid()) { + return false; + } + + QDateTime currentDateTime = QDateTime::currentDateTime(); + // 安装时间小于当前时间,差距超过48小时 + // 安装时间小于当前时间,差距在[48-0]小时内 + // 安装时间大于当前时间 + qint64 xt = currentDateTime.toSecsSinceEpoch() - installDate.toSecsSinceEpoch(); + return (xt >= 0) && (xt <= 48 * 3600); +} + +bool RecentlyInstalledModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const +{ + QDateTime leftInstallDate = QDateTime::fromString(source_left.data(DataEntity::InstallationTime).value(), "yyyy-MM-dd hh:mm:ss"); + QDateTime rightInstallDate = QDateTime::fromString(source_right.data(DataEntity::InstallationTime).value(), "yyyy-MM-dd hh:mm:ss"); + + qint64 xt = leftInstallDate.toSecsSinceEpoch() - rightInstallDate.toSecsSinceEpoch(); +// if (xt == 0) { +// return source_left.data(DataEntity::FirstLetter).value() < source_right.data(DataEntity::FirstLetter).value(); +// } + + return xt <= 0; +} + +bool RecentlyInstalledModel::event(QEvent *event) +{ + if (event->type() == QEvent::Timer) { + auto timerEvent = static_cast(event); + if (timerEvent->timerId() == m_timer->timerId()) { + invalidate(); + return true; + } + } + + return QObject::event(event); +} + +QVariant RecentlyInstalledModel::data(const QModelIndex &index, int role) const +{ + if (role == DataEntity::Group) { + return QStringLiteral("RecentlyInstalled"); + } + + return QSortFilterProxyModel::data(index, role); +} + +} // UkuiMenu diff --git a/src/libappdata/recently-installed-model.h b/src/libappdata/recently-installed-model.h new file mode 100644 index 0000000..abcfc39 --- /dev/null +++ b/src/libappdata/recently-installed-model.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2024, KylinSoft Co., Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: hxf + * + */ + +#ifndef UKUI_MENU_RECENTLY_INSTALLED_MODEL_H +#define UKUI_MENU_RECENTLY_INSTALLED_MODEL_H + +#include "app-list-plugin.h" +#include +class QTimer; + +namespace UkuiMenu { + +class RecentlyInstalledModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + explicit RecentlyInstalledModel(QObject *parent = nullptr); + bool event(QEvent *event) override; + + QVariant data(const QModelIndex &index, int role) const override; + +protected: + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; + bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override; + +private: + QTimer *m_timer {nullptr}; +}; + +} // UkuiMenu + +//Q_DECLARE_METATYPE(UkuiMenu::RecentlyInstalledModel*) + +#endif //UKUI_MENU_RECENTLY_INSTALLED_MODEL_H