feat(ai-search):AI搜索增加在模型未配置时的跳转模型配置页功能

This commit is contained in:
iaom 2024-11-27 16:38:19 +08:00
parent 491be7cbba
commit 7d1be5101c
12 changed files with 164 additions and 50 deletions

View File

@ -66,6 +66,9 @@ QVariant BestListModel::data(const QModelIndex &index, int role) const
case AdditionalRoles::ShowToolTip: {
return m_items.at(index.row())->showToolTip();
}
case AdditionalRoles::DisplayNameAsUrl: {
return m_items.at(index.row())->info().displayNameAsUrl;
}
default:
return {};
@ -97,6 +100,9 @@ const bool &BestListModel::isExpanded()
void BestListModel::appendInfo(const QString &pluginId, const SearchPluginIface::ResultInfo &info)
{
if (!info.showInBestMatch) {
return;
}
if (m_resourceTypes.contains(info.resourceType)) {
for (const auto& item : m_items) {
if (info.actionKey == item->info().actionKey && info.resourceType == item->info().resourceType) {

View File

@ -24,6 +24,7 @@ namespace UkuiSearch {
Q_NAMESPACE
enum AdditionalRoles {
ShowToolTip = Qt::UserRole + 1,
DisplayNameAsUrl = Qt::UserRole + 2,
};
Q_ENUM_NS(AdditionalRoles)

View File

@ -63,20 +63,23 @@ QVariant SearchResultModel::data(const QModelIndex &index, int role) const
return {};
}
switch(role) {
case Qt::DecorationRole: {
return m_items.at(index.row())->info().icon;
}
case Qt::DisplayRole: {
return m_items.at(index.row())->info().name;
}
case Qt::ToolTipRole: {
return m_items.at(index.row())->info().toolTip;
}
case AdditionalRoles::ShowToolTip: {
return m_items.at(index.row())->showToolTip();
}
default:
return {};
case Qt::DecorationRole: {
return m_items.at(index.row())->info().icon;
}
case Qt::DisplayRole: {
return m_items.at(index.row())->info().name;
}
case Qt::ToolTipRole: {
return m_items.at(index.row())->info().toolTip;
}
case AdditionalRoles::ShowToolTip: {
return m_items.at(index.row())->showToolTip();
}
case AdditionalRoles::DisplayNameAsUrl: {
return m_items.at(index.row())->info().displayNameAsUrl;
}
default:
return {};
}
}

View File

@ -73,7 +73,8 @@ void ResultViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
painter->save();
if(opt.state & QStyle::State_Selected) {
bool displayNameAsUrl = index.data(AdditionalRoles::DisplayNameAsUrl).toBool();
if (opt.state & QStyle::State_Selected) {
QVariant selectPen = opt.widget->property("textSelectPen");
if(selectPen.isValid() && selectPen.canConvert<QPen>()) {
m_hightLightEffectHelper->setTextColor(selectPen.value<QPen>().brush());
@ -81,8 +82,13 @@ void ResultViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
m_hightLightEffectHelper->setTextColor(QBrush(opt.palette.highlightedText().color()));
}
} else {
m_hightLightEffectHelper->setTextColor(QBrush(opt.palette.text().color()));
if (displayNameAsUrl) {
m_hightLightEffectHelper->setTextColor(QBrush(opt.palette.link().color()));
} else {
m_hightLightEffectHelper->setTextColor(QBrush(opt.palette.text().color()));
}
}
m_hightLightEffectHelper->setBold(!displayNameAsUrl);
m_textDoc->setPlainText(text);
m_hightLightEffectHelper->setDocument(m_textDoc);
@ -142,12 +148,19 @@ void HightLightEffectHelper::setTextColor(const QBrush &brush)
void HightLightEffectHelper::highlightBlock(const QString &text)
{
setFormat(0, text.length(), m_textCharFormat);
m_textCharFormat.setFontWeight(QFont::Bold);
int index = text.indexOf(m_expression);
while(index >= 0){
int length = m_expression.matchedLength();
setFormat(index, length, m_textCharFormat);
index = text.indexOf(m_expression, index+length);
if (m_need2BBolded) {
m_textCharFormat.setFontWeight(QFont::Bold);
int index = text.indexOf(m_expression);
while(index >= 0){
int length = m_expression.matchedLength();
setFormat(index, length, m_textCharFormat);
index = text.indexOf(m_expression, index+length);
}
}
m_textCharFormat.setFontWeight(QFont::Normal);
}
void HightLightEffectHelper::setBold(bool need2BBolded)
{
m_need2BBolded = need2BBolded;
}

View File

@ -38,13 +38,15 @@ public:
explicit HightLightEffectHelper(QObject *parent = nullptr);
void setExpression(const QString &text);
void setTextColor(const QBrush &brush);
void setBold(bool need2BBolded);
protected:
void highlightBlock(const QString &text);
void highlightBlock(const QString &text) override;
private:
QRegExp m_expression;
QTextCharFormat m_textCharFormat;
bool m_need2BBolded = true;
};
class ResultViewDelegate : public QStyledItemDelegate

View File

@ -274,18 +274,9 @@ void ResultView::onRowDoubleClickedSlot(const QModelIndex &index)
{
const SearchPluginIface::ResultInfo &info = m_model->getInfo(index);
SearchPluginIface *plugin = SearchPluginManager::getInstance()->getPlugin(m_plugin_id);
try {
if (plugin) {
if (!info.actionKey.isEmpty()) {
plugin->openAction(0, info.actionKey, info.type);
} else {
throw -2;
}
} else {
throw -1;
}
} catch(int e) {
qWarning()<<"Open failed, reason="<<e;
if (plugin) {
plugin->openAction(0, info.actionKey, info.type);
}
}
@ -296,7 +287,7 @@ void ResultView::onRowDoubleClickedSlot(const QModelIndex &index)
*/
void ResultView::onRowSelectedSlot(const QModelIndex &index)
{
if(index.isValid()) {
if (index.isValid()) {
m_is_selected = true;
this->setCurrentIndex(index);
Q_EMIT this->currentRowChanged(m_plugin_id, m_model->getInfo(index));

View File

@ -18,6 +18,7 @@ find_package(KF5WindowSystem)
find_package(qt5xdg)
find_package(ukui-file-metadata)
find_package(xapian)
find_package(ukui-quick COMPONENTS platform REQUIRED)
set(LIBUKUI_SEARCH_EXTERNAL_LIBS "")
set(LIBUKUI_SEARCH_PC_PKGS
@ -187,6 +188,7 @@ target_link_libraries(${PROJECT_NAME} PRIVATE
uchardet
xapian
ukui-file-metadata
ukui-quick::platform
${LIBUKUI_SEARCH_EXTERNAL_LIBS}
)

View File

@ -32,15 +32,13 @@
#include <QJsonDocument>
#include <QJsonArray>
#include <QFileInfo>
#include <app-launcher.h>
using namespace UkuiSearch;
size_t AiSearchPlugin::uniqueSymbolAi = 0;
QMutex AiSearchPlugin::mutex;
static const int THUMBNAIL_HEIGHT = 247;
static const int THUMBNAIL_WIDTH = 352;
bool AiSearch::s_failed = false;
DataManagementSession AiSearch::s_session = nullptr;
QMutex AiSearch::s_sessionMutex;
AiSearchPlugin::AiSearchPlugin(QObject *parent) : QObject(parent)
{
@ -54,11 +52,9 @@ AiSearchPlugin::AiSearchPlugin(QObject *parent) : QObject(parent)
m_timer = new QTimer(this);
m_timer->setInterval(500);
connect(m_timer, &QTimer::timeout, this, [ = ] {
if (FileIndexerConfig::getInstance()->isAiIndexEnable()) {
auto aiSearch = new AiSearch(m_searchResult, uniqueSymbolAi, m_keyword);
m_pool.start(aiSearch);
m_timer->stop();
}
auto aiSearch = new AiSearch(m_searchResult, uniqueSymbolAi, m_keyword);
m_pool.start(aiSearch);
m_timer->stop();
});
initDetailPage();
}
@ -106,12 +102,13 @@ QList<SearchPluginIface::Actioninfo> AiSearchPlugin::getActioninfo(int type)
void AiSearchPlugin::openAction(int actionkey, QString key, int type)
{
Q_UNUSED(type)
//TODO add some return message here.
switch (actionkey) {
case 0:
DataCollecter::collectLaunchEvent(QStringLiteral("AISearch"), QStringLiteral("openFile"));
if(FileUtils::openFile(key) == -1) {
if (type == 1) {
UkuiQuick::AppLauncher::instance()->launchAppWithArguments("ukui-control-center.desktop", QStringList() << "-m" << "search");
} else if (FileUtils::openFile(key) == -1) {
QMessageBox msgBox(m_detailPage);
msgBox.setModal(true);
msgBox.addButton(tr("OK"), QMessageBox::YesRole);
@ -134,9 +131,14 @@ void AiSearchPlugin::openAction(int actionkey, QString key, int type)
QWidget *AiSearchPlugin::detailPage(const SearchPluginIface::ResultInfo &ri)
{
if (!AiSearch::modelReady()) {
return m_detailPageWithoutModel;
}
if(ri.actionKey == m_currentActionKey) {
return m_detailPage;
}
auto creator = new ThumbnailCreator(ri.actionKey, QSize(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT), m_detailPage->window()->devicePixelRatio());
connect(creator, &ThumbnailCreator::ready, this, [&, ri](const QString& uri, const QImage &image){
if(uri != m_currentActionKey) {
@ -174,6 +176,32 @@ QWidget *AiSearchPlugin::detailPage(const SearchPluginIface::ResultInfo &ri)
void AiSearchPlugin::initDetailPage()
{
m_detailPageWithoutModel = new QWidget();
m_detailPageWithoutModel->setFixedWidth(360);
m_detailPageWithoutModel->setAttribute(Qt::WA_TranslucentBackground);
m_detailLytWithoutModel = new QVBoxLayout(m_detailPageWithoutModel);
m_detailPageWithoutModel->setLayout(m_detailLytWithoutModel);
m_detailLytWithoutModel->setContentsMargins(8, 0, 16, 0);
m_iconLabelWithoutModel = new QLabel(m_detailPageWithoutModel);
m_iconLabelWithoutModel->setAlignment(Qt::AlignCenter);
m_iconLabelWithoutModel->setPixmap(QIcon(":/res/icons/no-model-symbolic.svg").pixmap(128, 128));
m_actionFrameWithoutModel = new QFrame(m_detailPageWithoutModel);
m_actionFrameLytWithoutModel = new QVBoxLayout(m_actionFrameWithoutModel);
m_actionFrameLytWithoutModel->setContentsMargins(0, 0, 0, 0);
m_actionFrameLytWithoutModel->setAlignment(Qt::AlignCenter);
m_actionLabelWithoutModel = new ActionLabel(tr("click to config AI search"), "search", m_actionFrameWithoutModel);
m_actionLabelWithoutModel->adjustSize();
m_actionFrameLytWithoutModel->addWidget(m_actionLabelWithoutModel);
m_actionFrameWithoutModel->setLayout(m_actionFrameLytWithoutModel);
m_detailLytWithoutModel->addSpacing(146);
m_detailLytWithoutModel->addWidget(m_iconLabelWithoutModel);
m_detailLytWithoutModel->addWidget(m_actionFrameWithoutModel);
m_detailLytWithoutModel->addStretch();
m_detailPage = new QWidget();
m_detailPage->setFixedWidth(360);
m_detailPage->setAttribute(Qt::WA_TranslucentBackground);
@ -242,6 +270,10 @@ void AiSearchPlugin::initDetailPage()
m_detailPage->setLayout(m_detailLyt);
m_detailLyt->addStretch();
connect(m_actionLabelWithoutModel, &ActionLabel::actionTriggered, [ & ](){
openAction(0, m_currentActionKey, 1);
});
connect(m_actionLabel1, &ActionLabel::actionTriggered, [ & ](){
DataCollecter::collectLaunchEvent(QStringLiteral("AISearch"), QStringLiteral("openFile"));
if(FileUtils::openFile(m_currentActionKey) == -1) {
@ -270,10 +302,23 @@ AiSearchPlugin::~AiSearchPlugin()
m_thumbnailPool.waitForDone();
m_pool.waitForDone();
AiSearch::destroySession();
if(m_detailPage) {
delete m_detailPage;
m_detailPage = nullptr;
}
if(m_detailPageWithoutModel) {
delete m_detailPageWithoutModel;
m_detailPageWithoutModel = nullptr;
}
}
bool AiSearch::s_failed = false;
DataManagementSession AiSearch::s_session = nullptr;
QMutex AiSearch::s_sessionMutex;
AiSearch::AiSearch(DataQueue<SearchPluginIface::ResultInfo> *searchResult, size_t uniqueSymbol, const QString &keyword)
{
setAutoDelete(true);
m_searchResult = searchResult;
m_uniqueSymbol = uniqueSymbol;
m_keyword = keyword;
@ -289,6 +334,22 @@ void AiSearch::keywordSearch()
if (!AiSearch::createSession()) {
return;
}
if (!modelReady()) {
SearchPluginIface::ResultInfo ri;
ri.icon = QIcon(":/res/icons/no-model-symbolic.svg");
ri.name = QObject::tr("Click to complete the config for using ai search");
ri.toolTip = ri.name;
ri.type = 1;
ri.showInBestMatch = false;
ri.resourceType = QStringLiteral("file");
ri.displayNameAsUrl = true;
AiSearchPlugin::mutex.lock();
if (m_uniqueSymbol == AiSearchPlugin::uniqueSymbolAi) {
m_searchResult->enqueue(ri);
}
AiSearchPlugin::mutex.unlock();
return;
}
QJsonObject keyword;
keyword.insert(QStringLiteral("text"), m_keyword);
char* results = nullptr;
@ -322,6 +383,7 @@ void AiSearch::keywordSearch()
}
SearchPluginIface::ResultInfo ri;
ri.type = 0;
if (createResultInfo(ri, aResult.toObject().value("filepath").toString())) {
AiSearchPlugin::mutex.lock();
if (m_uniqueSymbol == AiSearchPlugin::uniqueSymbolAi) {
@ -375,3 +437,19 @@ void AiSearch::destroySession() {
}
s_session = nullptr;
}
bool AiSearch::modelReady()
{
FeatureStatus status;
DataManagementResult result = data_management_get_feature_status(s_session, &status);
if (result == DataManagementResult::DATA_MANAGEMENT_SUCCESS) {
if (status == FeatureStatus::AVAILABLE) {
return true;
} else {
qWarning() << "AI search Model is not available";
}
} else {
qWarning() << "AI search: Get feature status failed, result is" << result;
}
return false;
}

View File

@ -70,6 +70,13 @@ public:
private:
void initDetailPage();
QString m_currentActionKey;
QWidget *m_detailPageWithoutModel;
QVBoxLayout *m_detailLytWithoutModel = nullptr;
QLabel *m_iconLabelWithoutModel = nullptr;
QFrame *m_actionFrameWithoutModel = nullptr;
QVBoxLayout *m_actionFrameLytWithoutModel = nullptr;
ActionLabel *m_actionLabelWithoutModel = nullptr;
QWidget *m_detailPage;
QVBoxLayout *m_detailLyt = nullptr;
QLabel *m_iconLabel = nullptr;
@ -112,11 +119,11 @@ public:
~AiSearch() override;
static bool createSession();
static void destroySession();
static DataManagementSession s_session;
static bool s_failed;
static QMutex s_sessionMutex;
static bool modelReady();
protected:
void run() override;
private:
void keywordSearch();
bool createResultInfo(SearchPluginIface::ResultInfo &ri, const QString& path);
@ -124,6 +131,9 @@ private:
DataQueue<SearchPluginIface::ResultInfo> *m_searchResult = nullptr;
size_t m_uniqueSymbol;
QString m_keyword;
static QMutex s_sessionMutex;
static bool s_failed;
static DataManagementSession s_session;
};
}

View File

@ -67,13 +67,17 @@ public:
QString actionKey; //执行action时的key
QString resourceType; //资源类型可定义例如“file, app”
int type;
bool showInBestMatch = true;
bool displayNameAsUrl = false;
explicit ResultInfo(const QIcon &iconToSet = QIcon(),
const QString &nameToSet = QString(),
const QString &toolTipToSet = QString(),
const QString &resourceTypeToSet = QString(),
const QVector<DescriptionInfo> &descriptionToSet = QVector<DescriptionInfo>(),
const QString &actionKeyToSet = QString(),
const int &typeToSet = 0) {
const int &typeToSet = 0,
const bool showInBestMatchToSet = true,
const bool displayNameAsUrlToSet = false) {
icon = iconToSet;
name = nameToSet;
toolTip = toolTipToSet;
@ -81,6 +85,8 @@ public:
description = descriptionToSet;
actionKey = actionKeyToSet;
type = typeToSet;
showInBestMatch = showInBestMatchToSet;
displayNameAsUrl = displayNameAsUrlToSet;
}
~ResultInfo() {
description.clear();

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@ -10,5 +10,6 @@
<file>res/icons/search-web-default.svg</file>
<file>res/icons/unknown.svg</file>
<file>res/icons/ukui-control-center.svg</file>
<file>res/icons/no-model-symbolic.svg</file>
</qresource>
</RCC>