feat: 修改后端使用model实现

This commit is contained in:
hewenfei 2023-12-12 10:12:16 +08:00
parent d201ead078
commit 2d08482c4c
5 changed files with 634 additions and 0 deletions

View File

@ -129,6 +129,8 @@ set(SOURCE_FILES
src/extension/favorite/favorite-extension-plugin.cpp src/extension/favorite/favorite-extension-plugin.h
src/extension/favorite/favorite-context-menu.cpp src/extension/favorite/favorite-context-menu.h
src/extension/favorite/favorite-widget.cpp src/extension/favorite/favorite-widget.h
src/libappdata/basic-app-model.cpp src/libappdata/basic-app-model.h
src/libappdata/app-database-interface.cpp src/libappdata/app-database-interface.h
)

View File

@ -0,0 +1,334 @@
//
// Created by hxf on 23-12-11.
//
#include "app-database-interface.h"
#include "user-config.h"
#include "settings.h"
#include <application-info.h>
#include <QDebug>
#define APP_ICON_PREFIX "image://appicon/"
namespace UkuiMenu {
class AppDatabaseWorkerPrivate : public QObject
{
Q_OBJECT
friend class AppDatabaseInterface;
public:
explicit AppDatabaseWorkerPrivate(AppDatabaseInterface *parent = nullptr);
DataEntityVector getAllApps();
// 数据库操作函数
void setAppProperty(const QString &appid, const UkuiSearch::ApplicationPropertyMap &propertyMap);
void setAppProperty(const QString &appid, const UkuiSearch::ApplicationProperty::Property &property, const QVariant &value);
private Q_SLOTS:
/**
*
* @param infos id列表
*/
void onAppDatabaseAdded(const QStringList &infos);
void onAppDatabaseUpdate(const UkuiSearch::ApplicationInfoMap &infoMap);
void onAppDatabaseUpdateAll(const QStringList &infos);
void onAppDatabaseDeleted(const QStringList &infos);
private:
static void addInfoToApp(const QMap<UkuiSearch::ApplicationProperty::Property, QVariant> &info, DataEntity &app);
bool isFiltered(const UkuiSearch::ApplicationPropertyMap &appInfo) const;
private:
AppDatabaseInterface *q {nullptr};
UkuiSearch::ApplicationInfo *appDatabase {nullptr};
UkuiSearch::ApplicationProperties properties;
UkuiSearch::ApplicationPropertyMap filter;
};
AppDatabaseWorkerPrivate::AppDatabaseWorkerPrivate(AppDatabaseInterface *parent) : QObject(parent), q(parent)
{
// 注册需要在信号和槽函数中使用的数据结构
qRegisterMetaType<UkuiMenu::DataEntityVector>("DataEntityVector");
// 初始化应用数据库链接
appDatabase = new UkuiSearch::ApplicationInfo(this);
// 首次启动时,为某些应用设置标志位
if (UserConfig::instance()->isFirstStartUp()) {
// 默认收藏应用
for (const auto &appid : GlobalSetting::instance()->defaultFavoriteApps()) {
appDatabase->setAppToFavorites(appid);
}
}
// 设置从数据库查询哪些属性
properties << UkuiSearch::ApplicationProperty::Property::Top
<< UkuiSearch::ApplicationProperty::Property::Lock
<< UkuiSearch::ApplicationProperty::Property::Favorites
<< UkuiSearch::ApplicationProperty::Property::LaunchTimes
<< UkuiSearch::ApplicationProperty::Property::DesktopFilePath
<< UkuiSearch::ApplicationProperty::Property::Icon
<< UkuiSearch::ApplicationProperty::Property::LocalName
<< UkuiSearch::ApplicationProperty::Property::Category
<< UkuiSearch::ApplicationProperty::Property::FirstLetterAll
<< UkuiSearch::ApplicationProperty::Property::DontDisplay
<< UkuiSearch::ApplicationProperty::Property::AutoStart
<< UkuiSearch::ApplicationProperty::Property::InsertTime
<< UkuiSearch::ApplicationProperty::Property::Launched;
// 需要从数据库过滤的属性
filter.insert(UkuiSearch::ApplicationProperty::Property::DontDisplay, 0);
filter.insert(UkuiSearch::ApplicationProperty::Property::AutoStart, 0);
// 链接数据库信号
connect(appDatabase, &UkuiSearch::ApplicationInfo::appDBItems2BAdd, this, &AppDatabaseWorkerPrivate::onAppDatabaseAdded);
connect(appDatabase, &UkuiSearch::ApplicationInfo::appDBItems2BUpdate, this, &AppDatabaseWorkerPrivate::onAppDatabaseUpdate);
connect(appDatabase, &UkuiSearch::ApplicationInfo::appDBItems2BUpdateAll, this, &AppDatabaseWorkerPrivate::onAppDatabaseUpdateAll);
connect(appDatabase, &UkuiSearch::ApplicationInfo::appDBItems2BDelete, this, &AppDatabaseWorkerPrivate::onAppDatabaseDeleted);
connect(appDatabase, &UkuiSearch::ApplicationInfo::DBOpenFailed, q, &AppDatabaseInterface::appDatabaseOpenFailed);
}
void AppDatabaseWorkerPrivate::addInfoToApp(const UkuiSearch::ApplicationPropertyMap &info, DataEntity &app)
{
app.setTop(info.value(UkuiSearch::ApplicationProperty::Property::Top).toInt());
app.setLock(info.value(UkuiSearch::ApplicationProperty::Property::Lock).toInt() == 1);
app.setFavorite(info.value(UkuiSearch::ApplicationProperty::Property::Favorites).toInt());
app.setLaunchTimes(info.value(UkuiSearch::ApplicationProperty::Property::LaunchTimes).toInt());
app.setId(info.value(UkuiSearch::ApplicationProperty::Property::DesktopFilePath).toString());
app.setIcon(APP_ICON_PREFIX + info.value(UkuiSearch::ApplicationProperty::Property::Icon).toString());
app.setName(info.value(UkuiSearch::ApplicationProperty::Property::LocalName).toString());
app.setCategory(info.value(UkuiSearch::ApplicationProperty::Property::Category).toString());
app.setFirstLetter(info.value(UkuiSearch::ApplicationProperty::Property::FirstLetterAll).toString());
app.setInsertTime(info.value(UkuiSearch::ApplicationProperty::Property::InsertTime).toString());
app.setLaunched(info.value(UkuiSearch::ApplicationProperty::Property::Launched).toInt());
}
bool AppDatabaseWorkerPrivate::isFiltered(const UkuiSearch::ApplicationPropertyMap &appInfo) const
{
QMapIterator<UkuiSearch::ApplicationProperty::Property, QVariant> iterator(filter);
while (iterator.hasNext()) {
iterator.next();
if (appInfo.value(iterator.key()) == iterator.value()) {
return true;
}
}
return false;
}
DataEntityVector AppDatabaseWorkerPrivate::getAllApps()
{
UkuiSearch::ApplicationInfoMap appInfos = appDatabase->getInfo(properties, filter);
if (appInfos.isEmpty()) {
return {};
}
DataEntityVector apps;
for (const auto &info : appInfos) {
DataEntity app;
AppDatabaseWorkerPrivate::addInfoToApp(info, app);
apps.append(app);
}
return apps;
}
void AppDatabaseWorkerPrivate::onAppDatabaseAdded(const QStringList &infos)
{
if (infos.isEmpty()) {
return;
}
DataEntityVector apps;
for (const QString &appid : infos) {
const UkuiSearch::ApplicationPropertyMap appInfo = appDatabase->getInfo(appid, properties);
if (isFiltered(appInfo)) {
continue;
}
DataEntity app;
addInfoToApp(appInfo, app);
apps.append(app);
}
if (apps.isEmpty()) {
return;
}
Q_EMIT q->appAdded(apps);
}
void AppDatabaseWorkerPrivate::onAppDatabaseUpdate(const UkuiSearch::ApplicationInfoMap &infoMap)
{
if (infoMap.isEmpty()) {
return;
}
QVector<QPair<DataEntity, QVector<int> > > updates;
QMapIterator<QString, UkuiSearch::ApplicationPropertyMap> iterator(infoMap);
while (iterator.hasNext()) {
iterator.next();
DataEntity app;
QVector<int> roles;
QMapIterator<UkuiSearch::ApplicationProperty::Property, QVariant> it(iterator.value());
while (it.hasNext()) {
it.next();
switch (it.key()) {
case UkuiSearch::ApplicationProperty::LocalName:
app.setName(it.value().toString());
roles.append(DataEntity::Name);
break;
case UkuiSearch::ApplicationProperty::FirstLetterAll:
app.setFirstLetter(it.value().toString());
roles.append(DataEntity::FirstLetter);
break;
case UkuiSearch::ApplicationProperty::Icon:
app.setIcon(it.value().toString());
roles.append(DataEntity::Icon);
break;
case UkuiSearch::ApplicationProperty::InsertTime:
app.setInsertTime(it.value().toString());
roles.append(DataEntity::Icon);
break;
case UkuiSearch::ApplicationProperty::Category:
app.setCategory(it.value().toString());
roles.append(DataEntity::Category);
break;
case UkuiSearch::ApplicationProperty::LaunchTimes:
app.setLaunchTimes(it.value().toInt());
roles.append(DataEntity::LaunchTimes);
break;
case UkuiSearch::ApplicationProperty::Favorites:
app.setFavorite(it.value().toInt());
roles.append(DataEntity::Favorite);
break;
case UkuiSearch::ApplicationProperty::Launched:
app.setLaunched(it.value().toInt());
roles.append(DataEntity::IsLaunched);
break;
case UkuiSearch::ApplicationProperty::Top:
app.setTop(it.value().toInt());
roles.append(DataEntity::Top);
break;
case UkuiSearch::ApplicationProperty::Lock:
app.setLock(it.value().toBool());
roles.append(DataEntity::IsLocked);
break;
default:
break;
}
}
// 这个函数中,没有更新列不会发送信号
if (roles.isEmpty()) {
continue;
}
app.setId(iterator.key());
updates.append({app, roles});
}
Q_EMIT q->appUpdated(updates);
}
void AppDatabaseWorkerPrivate::onAppDatabaseDeleted(const QStringList &infos)
{
if (infos.empty()) {
return;
}
Q_EMIT q->appDeleted(infos);
}
void AppDatabaseWorkerPrivate::onAppDatabaseUpdateAll(const QStringList &infos)
{
if (infos.isEmpty()) {
return;
}
QVector<QPair<DataEntity, QVector<int> > > updates;
DataEntityVector apps;
for (const auto &appid : infos) {
const UkuiSearch::ApplicationPropertyMap appInfo = appDatabase->getInfo(appid, properties);
DataEntity app;
addInfoToApp(appInfo, app);
apps.append(app);
updates.append({app, {}});
}
Q_EMIT q->appUpdated(updates);
}
void AppDatabaseWorkerPrivate::setAppProperty(const QString &appid, const UkuiSearch::ApplicationPropertyMap &propertyMap)
{
QMapIterator<UkuiSearch::ApplicationProperty::Property, QVariant> iterator(propertyMap);
while (iterator.hasNext()) {
iterator.next();
setAppProperty(appid, iterator.key(), iterator.value());
}
}
void AppDatabaseWorkerPrivate::setAppProperty(const QString &appid, const UkuiSearch::ApplicationProperty::Property &property,
const QVariant &value)
{
switch (property) {
case UkuiSearch::ApplicationProperty::Favorites:
appDatabase->setFavoritesOfApp(appid, value.toInt());
break;
case UkuiSearch::ApplicationProperty::Launched:
appDatabase->setAppLaunchedState(appid, value.toBool());
break;
case UkuiSearch::ApplicationProperty::Top:
appDatabase->setTopOfApp(appid, value.toInt());
break;
default:
break;
}
}
// ====== AppDatabaseInterface ====== //
AppDatabaseInterface::AppDatabaseInterface(QObject *parent) : QObject(parent), d(new AppDatabaseWorkerPrivate(this))
{
}
DataEntityVector AppDatabaseInterface::apps() const
{
return d->getAllApps();
}
void AppDatabaseInterface::fixAppToTop(const QString &appid, int index)
{
if (index < 0) {
index = 0;
}
d->setAppProperty(appid, UkuiSearch::ApplicationProperty::Top, index);
}
void AppDatabaseInterface::fixAppToFavorite(const QString &appid, int index)
{
if (index < 0) {
index = 0;
}
d->setAppProperty(appid, UkuiSearch::ApplicationProperty::Favorites, index);
}
void AppDatabaseInterface::updateApLaunchedState(const QString &appid, bool state)
{
d->setAppProperty(appid, UkuiSearch::ApplicationProperty::Launched, state);
}
} // UkuiMenu
#include "app-database-interface.moc"

View File

@ -0,0 +1,85 @@
//
// Created by hxf on 23-12-11.
//
#ifndef UKUI_MENU_APP_DATABASE_INTERFACE_H
#define UKUI_MENU_APP_DATABASE_INTERFACE_H
#include <QObject>
#include <QPair>
#include "data-entity.h"
namespace UkuiMenu {
typedef QVector<DataEntity> DataEntityVector;
/**
*
*/
class AppDatabaseWorkerPrivate;
class AppDatabaseInterface : public QObject
{
Q_OBJECT
public:
explicit AppDatabaseInterface(QObject *parent = nullptr);
/**
*
* @return
*/
DataEntityVector apps() const;
/**
*
* @param appid id
* @param index 0
*/
void fixAppToTop(const QString &appid, int index);
/**
*
* @param appid id
* @param index 0
*/
void fixAppToFavorite(const QString &appid, int index);
/**
*
* @param state true已被启动false未被启动
*/
void updateApLaunchedState(const QString &appid, bool state = true);
Q_SIGNALS:
/**
*
* @param apps
*/
void appAdded(const UkuiMenu::DataEntityVector &apps);
/**
*
* @param updates
*/
void appUpdated(const QVector<QPair<UkuiMenu::DataEntity, QVector<int> > > &updates);
/**
*
* @param apps id列表
*/
void appDeleted(const QStringList &apps);
/**
*
*/
void appDatabaseOpenFailed();
private:
AppDatabaseWorkerPrivate *d {nullptr};
};
} // UkuiMenu
Q_DECLARE_METATYPE(UkuiMenu::DataEntityVector)
#endif //UKUI_MENU_APP_DATABASE_INTERFACE_H

View File

@ -0,0 +1,164 @@
//
// Created by hxf on 23-12-11.
//
#include "basic-app-model.h"
#include <QDebug>
namespace UkuiMenu {
BasicAppModel::BasicAppModel(QObject *parent) : QAbstractListModel(parent)
, m_databaseInterface(new AppDatabaseInterface(this))
{
connect(m_databaseInterface, &AppDatabaseInterface::appDatabaseOpenFailed, this, [this] {
qWarning() << "BasicAppModel: app database open failed.";
m_apps.clear();
// TODO: 显示错误信息到界面
});
m_apps = m_databaseInterface->apps();
connect(m_databaseInterface, &AppDatabaseInterface::appAdded, this, &BasicAppModel::onAppAdded);
connect(m_databaseInterface, &AppDatabaseInterface::appUpdated, this, &BasicAppModel::onAppUpdated);
connect(m_databaseInterface, &AppDatabaseInterface::appDeleted, this, &BasicAppModel::onAppDeleted);
}
int BasicAppModel::rowCount(const QModelIndex &parent) const
{
return m_apps.count();
}
int BasicAppModel::columnCount(const QModelIndex &parent) const
{
return 1;
}
QVariant BasicAppModel::data(const QModelIndex &index, int role) const
{
if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid)) {
return {};
}
const DataEntity &app = m_apps[index.row()];
return app.getValue(static_cast<DataEntity::PropertyName>(role));
}
bool BasicAppModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid)) {
return false;
}
DataEntity &app = m_apps[index.row()];
DataEntity::PropertyName propertyName;
switch (propertyName) {
case DataEntity::Group:
app.setGroup(value.toString());
return true;
case DataEntity::IsLaunched: {
if (value.toBool() == app.launched()) {
return false;
}
m_databaseInterface->updateApLaunchedState(app.id(), value.toBool());
return true;
}
case DataEntity::Favorite:
if (value.toInt() == app.favorite()) {
return false;
}
m_databaseInterface->fixAppToFavorite(app.id(), value.toInt());
return true;
case DataEntity::Top:
if (value.toInt() == app.top()) {
return false;
}
m_databaseInterface->fixAppToTop(app.id(), value.toInt());
return true;
case DataEntity::RecentInstall:
app.setRecentInstall(value.toBool());
return true;
default:
break;
}
return QAbstractItemModel::setData(index, value, role);
}
QHash<int, QByteArray> BasicAppModel::roleNames() const
{
return DataEntity::AppRoleNames();
}
AppDatabaseInterface *BasicAppModel::databaseInterface() const
{
return m_databaseInterface;
}
void BasicAppModel::onAppAdded(const DataEntityVector &apps)
{
beginInsertRows(QModelIndex(), m_apps.size(), m_apps.size() + apps.size() - 1);
m_apps.append(apps);
endInsertRows();
}
void BasicAppModel::onAppUpdated(const QVector<QPair<DataEntity, QVector<int> > > &updates)
{
for (const auto &pair : updates) {
int index = indexOfApp(pair.first.id());
if (index < 0) {
continue;
}
DataEntity &app = m_apps[index];
QVector<int> roles = pair.second;
if (!roles.isEmpty()) {
for (const auto &role : roles) {
app.setValue(static_cast<DataEntity::PropertyName>(role), static_cast<DataEntity::PropertyName>(role));
}
} else {
app = pair.first;
}
dataChanged(QAbstractListModel::index(index), QAbstractListModel::index(index), roles);
}
}
void BasicAppModel::onAppDeleted(const QStringList &apps)
{
for (const auto &appid : apps) {
int index = indexOfApp(appid);
if (index < 0) {
continue;
}
beginRemoveRows(QModelIndex(), index, index);
m_apps.takeAt(index);
endRemoveRows();
}
}
int BasicAppModel::indexOfApp(const QString &appid) const
{
auto it = std::find_if(m_apps.constBegin(), m_apps.constEnd(), [&appid] (const DataEntity &app) {
return app.id() == appid;
});
if (it == m_apps.constEnd()) {
return -1;
}
return std::distance(m_apps.constBegin(), it);
}
DataEntity BasicAppModel::appOfIndex(int row) const
{
QModelIndex idx = QAbstractListModel::index(row);
if (!checkIndex(idx, QAbstractItemModel::CheckIndexOption::IndexIsValid)) {
return {};
}
return m_apps.at(row);
}
} // UkuiMenu

View File

@ -0,0 +1,49 @@
//
// Created by hxf on 23-12-11.
//
#ifndef UKUI_MENU_BASIC_APP_MODEL_H
#define UKUI_MENU_BASIC_APP_MODEL_H
#include <QAbstractListModel>
#include <QVector>
#include "data-entity.h"
#include "app-database-interface.h"
namespace UkuiMenu {
class BasicAppModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit BasicAppModel(QObject *parent = nullptr);
/**
* 访
* @return @AppDatabaseInterface *
*/
AppDatabaseInterface *databaseInterface() const;
DataEntity appOfIndex(int row) const;
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
QHash<int, QByteArray> roleNames() const override;
private Q_SLOTS:
void onAppAdded(const UkuiMenu::DataEntityVector &apps);
void onAppUpdated(const QVector<QPair<UkuiMenu::DataEntity, QVector<int> > > &updates);
void onAppDeleted(const QStringList &apps);
private:
int indexOfApp(const QString &appid) const;
AppDatabaseInterface *m_databaseInterface {nullptr};
QVector<DataEntity> m_apps;
};
} // UkuiMenu
#endif //UKUI_MENU_BASIC_APP_MODEL_H