feat(ai-search):AI搜索增加在模型未配置时的跳转模型配置页功能
This commit is contained in:
parent
491be7cbba
commit
7d1be5101c
|
@ -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) {
|
||||
|
|
|
@ -24,6 +24,7 @@ namespace UkuiSearch {
|
|||
Q_NAMESPACE
|
||||
enum AdditionalRoles {
|
||||
ShowToolTip = Qt::UserRole + 1,
|
||||
DisplayNameAsUrl = Qt::UserRole + 2,
|
||||
};
|
||||
Q_ENUM_NS(AdditionalRoles)
|
||||
|
||||
|
|
|
@ -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 {};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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}
|
||||
)
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 |
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue