加入应用列表的搜索功能

This commit is contained in:
RigoLigoRLC 2021-09-06 23:49:15 +08:00
parent f5649a121f
commit f96aa8150a
7 changed files with 129 additions and 12 deletions

@ -70,6 +70,7 @@ set(SOURCE_FILES
inc/spkappitem.h gui/spkappitem.cpp inc/spkappitem.h gui/spkappitem.cpp
inc/spkpopup.h gui/spkpopup.cpp inc/spkpopup.h gui/spkpopup.cpp
inc/spkstretchlayout.h gui/spkstretchlayout.cpp inc/spkstretchlayout.h gui/spkstretchlayout.cpp
inc/spkfocuslineedit.h
inc/page/spkpagebase.h gui/page/spkpagebase.cpp inc/page/spkpagebase.h gui/page/spkpagebase.cpp
inc/page/spkpageuitest.h gui/page/spkpageuitest.cpp inc/page/spkpageuitest.h gui/page/spkpageuitest.cpp

@ -114,9 +114,10 @@ namespace SpkUi
} }
} }
void SpkPageAppList::SetPageStatus(int total, int current, int itemCount) void SpkPageAppList::SetPageStatus(int total, int current, int itemCount, QString &keyword)
{ {
mCurrentPage = current; mCurrentPage = current;
mKeyword = keyword;
mPageIndicator->setText(tr("Page %1 / %2, %3 apps in total") mPageIndicator->setText(tr("Page %1 / %2, %3 apps in total")
.arg(current).arg(total).arg(itemCount)); .arg(current).arg(total).arg(itemCount));
mBtnPgUp->setDisabled(current == 1); mBtnPgUp->setDisabled(current == 1);
@ -135,13 +136,19 @@ namespace SpkUi
void SpkPageAppList::PageUp() void SpkPageAppList::PageUp()
{ {
DisablePageSwitchers(); DisablePageSwitchers();
emit SwitchListPage(mCategoryId, mCurrentPage - 1); if(mKeyword.isEmpty())
emit SwitchListPage(mCategoryId, mCurrentPage - 1);
else
emit SwitchSearchPage(mKeyword, mCurrentPage - 1);
} }
void SpkPageAppList::PageDown() void SpkPageAppList::PageDown()
{ {
DisablePageSwitchers(); DisablePageSwitchers();
emit SwitchListPage(mCategoryId, mCurrentPage + 1); if(mKeyword.isEmpty())
emit SwitchListPage(mCategoryId, mCurrentPage + 1);
else
emit SwitchSearchPage(mKeyword, mCurrentPage + 1);
} }
void SpkPageAppList::GotoPage() void SpkPageAppList::GotoPage()
@ -153,7 +160,10 @@ namespace SpkUi
return SpkUiMessage::SendStoreNotification(tr("Page %1 is not a valid page number!") return SpkUiMessage::SendStoreNotification(tr("Page %1 is not a valid page number!")
.arg(page)); .arg(page));
DisablePageSwitchers(); DisablePageSwitchers();
emit SwitchListPage(mCategoryId, page); if(mKeyword.isEmpty())
emit SwitchListPage(mCategoryId, page);
else
emit SwitchSearchPage(mKeyword, page);
} }
void SpkPageAppList::Activated() void SpkPageAppList::Activated()

@ -116,11 +116,41 @@ void SpkMainWindow::CategoryListDataReceived()
return; return;
} }
SwitchToPage(SpkUi::PgAppList); SwitchToPage(SpkUi::PgAppList);
PopulateAppList(retval.toObject()); PopulateAppList(retval.toObject(), "");
setCursor(Qt::ArrowCursor); setCursor(Qt::ArrowCursor);
} }
void SpkMainWindow::PopulateAppList(QJsonObject appData) void SpkMainWindow::SearchKeyword(QString aKeyword, int aPage)
{
using namespace SpkUtils;
VerifySingleRequest(mCategoryAppListGetReply);
QJsonObject reqData;
QJsonDocument reqDoc;
reqData.insert("application_name", QJsonValue(aKeyword));
reqData.insert("page", QJsonValue(aPage));
reqDoc.setObject(reqData);
mCategoryAppListGetReply = STORE->SendApiRequest("application/get_application_list", reqDoc);
mCategoryAppListGetReply->setProperty("keyword", aKeyword);
DeleteReplyLater(mCategoryAppListGetReply);
connect(mCategoryAppListGetReply, &QNetworkReply::finished,
this, &SpkMainWindow::SearchDataReceived);
setCursor(Qt::BusyCursor);
}
void SpkMainWindow::SearchDataReceived()
{
QJsonValue retval;
if(!SpkUtils::VerifyReplyJson(mCategoryAppListGetReply, retval) || !retval.isObject())
{
sErrPop(tr("Failed to search keyword! Type of retval: %1.").arg(retval.type()));
return;
}
SwitchToPage(SpkUi::PgAppList);
PopulateAppList(retval.toObject(), mCategoryAppListGetReply->property("keyword").toString());
setCursor(Qt::ArrowCursor);
}
void SpkMainWindow::PopulateAppList(QJsonObject appData, QString &&keyword)
{ {
auto w = ui->PageAppList; auto w = ui->PageAppList;
w->ClearAll(); w->ClearAll();
@ -141,7 +171,7 @@ void SpkMainWindow::PopulateAppList(QJsonObject appData)
if(appData.contains("count") && appData.value("count").isDouble()) if(appData.contains("count") && appData.value("count").isDouble())
totalApps = appData.value("count").toInt(); totalApps = appData.value("count").toInt();
else return err(); else return err();
w->SetPageStatus(pgTotal, pgCurrent, totalApps); w->SetPageStatus(pgTotal, pgCurrent, totalApps, keyword);
if(!appData.contains("data") || !appData.value("data").isArray()) if(!appData.contains("data") || !appData.value("data").isArray())
return err(); return err();
@ -184,6 +214,10 @@ void SpkMainWindow::Initialize()
this, &SpkMainWindow::EnterCategoryList); this, &SpkMainWindow::EnterCategoryList);
connect(ui->PageAppList, &SpkUi::SpkPageAppList::SwitchListPage, connect(ui->PageAppList, &SpkUi::SpkPageAppList::SwitchListPage,
this, &SpkMainWindow::EnterCategoryList); this, &SpkMainWindow::EnterCategoryList);
connect(ui->PageAppList, &SpkUi::SpkPageAppList::SwitchSearchPage,
this, &SpkMainWindow::SearchKeyword);
connect(ui->SearchEdit, &QLineEdit::returnPressed,
[=](){ emit SearchKeyword(ui->SearchEdit->text(), 1); });
} }
// ==================== Main Widget Initialization ==================== // ==================== Main Widget Initialization ====================
@ -282,6 +316,45 @@ SpkUi::SpkMainWidget::SpkMainWidget(QWidget *parent) : QFrame(parent)
HorizontalDivide->addWidget(SideBarRestrictor); HorizontalDivide->addWidget(SideBarRestrictor);
HorizontalDivide->addLayout(VLayMain); HorizontalDivide->addLayout(VLayMain);
//============ Search Bar ============
SearchEdit = new SpkFocusLineEdit(this);
SearchEdit->setPlaceholderText(tr("Press Enter to search"));
SearchEdit->setFixedWidth(30);
SearchEdit->setFixedHeight(30);
SearchBarAnim = new QTimeLine(300, this);
SearchBarAnim->setDuration(300);
SearchBarAnim->setEasingCurve(QEasingCurve::OutExpo);
SearchBarAnim->setUpdateInterval(20);
ActClearSearchBar = SearchEdit->addAction(QIcon(":/icons/clear-input.svg"),
QLineEdit::TrailingPosition);
ActClearSearchBar->setVisible(false); // Invisible by default
ActSearchIcon = SearchEdit->addAction(QIcon(":/icons/search-mini.svg"), QLineEdit::LeadingPosition);
connect(SearchEdit, &SpkFocusLineEdit::focusGained,
[=](){
ActClearSearchBar->setVisible(true);
SearchBarAnim->setDirection(QTimeLine::Forward);
SearchBarAnim->start();
});
connect(SearchEdit, &SpkFocusLineEdit::focusLost,
[=](){
ActClearSearchBar->setVisible(false);
SearchBarAnim->setDirection(QTimeLine::Backward);
SearchBarAnim->start();
});
connect(SearchBarAnim, &QTimeLine::valueChanged,
[=](qreal v){
SearchEdit->setFixedWidth(static_cast<int>(250 * v) + 30);
});
connect(ActClearSearchBar, &QAction::triggered, [=](){ SearchEdit->clear(); });
auto space = TitleBar->GetUserSpace();
space->addWidget(SearchEdit);
space->addStretch();
//============ Pages =============
// Red-Black tree based map will be able to sort things // Red-Black tree based map will be able to sort things
QMap<SpkStackedPages, QWidget*> sorter; QMap<SpkStackedPages, QWidget*> sorter;

@ -18,7 +18,7 @@ namespace SpkUi
void AddApplicationEntry(QString name, QString pkgName, QString description, QString iconUrl, void AddApplicationEntry(QString name, QString pkgName, QString description, QString iconUrl,
int appId); int appId);
void ClearAll(); void ClearAll();
void SetPageStatus(int total, int current, int itemCount); void SetPageStatus(int total, int current, int itemCount, QString &keyword);
void SetCurrentCategory(int categoryId) { mCategoryId = categoryId; } void SetCurrentCategory(int categoryId) { mCategoryId = categoryId; }
private: private:
@ -40,10 +40,12 @@ namespace SpkUi
QIntValidator *mPageValidator; QIntValidator *mPageValidator;
int mCategoryId, mCurrentPage; int mCategoryId, mCurrentPage;
QString mKeyword;
signals: signals:
void ApplicationClicked(QString name, QString pkgName); void ApplicationClicked(QString name, QString pkgName);
void SwitchListPage(int categoryId, int page); void SwitchListPage(int categoryId, int page);
void SwitchSearchPage(QString keyword, int page);
public slots: public slots:
void ResourceAcquisitionFinished(int id, ResourceResult result); void ResourceAcquisitionFinished(int id, ResourceResult result);

19
inc/spkfocuslineedit.h Normal file

@ -0,0 +1,19 @@
#pragma once
#include <QtWidgets/QLineEdit>
class SpkFocusLineEdit final : public QLineEdit
{
Q_OBJECT
public:
explicit SpkFocusLineEdit(QWidget *parent = nullptr) : QLineEdit(parent) {}
protected:
void focusInEvent(QFocusEvent *e) override { emit focusGained(); }
void focusOutEvent(QFocusEvent *e) override { emit focusLost(); }
signals:
void focusGained();
void focusLost();
};

@ -12,8 +12,10 @@
#include <QJsonObject> #include <QJsonObject>
#include "spksidebartree.h" // In place of #include <QTreeWidget> #include "spksidebartree.h" // In place of #include <QTreeWidget>
#include <QPointer> #include <QPointer>
#include "inc/page/spkpageuitest.h" #include <QTimeLine>
#include "inc/page/spkpageapplist.h" #include "spkfocuslineedit.h"
#include "page/spkpageuitest.h"
#include "page/spkpageapplist.h"
class QNetworkReply; class QNetworkReply;
@ -146,6 +148,11 @@ namespace SpkUi
QTreeWidgetItem *CategoryParentItem; QTreeWidgetItem *CategoryParentItem;
// Title bar search bar
SpkFocusLineEdit *SearchEdit;
QAction *ActClearSearchBar, *ActSearchIcon;
QTimeLine *SearchBarAnim;
//Pages //Pages
SpkPageUiTest *PageQssTest; SpkPageUiTest *PageQssTest;
SpkPageAppList *PageAppList; SpkPageAppList *PageAppList;
@ -178,10 +185,13 @@ class SpkMainWindow : public SpkWindow
void SwitchToPage(SpkUi::SpkStackedPages page); void SwitchToPage(SpkUi::SpkStackedPages page);
void CategoryDataReceived(); void CategoryDataReceived();
// Enter a category (and switch pages)
void EnterCategoryList(int aCategoryId, int aPage); void EnterCategoryList(int aCategoryId, int aPage);
void CategoryListDataReceived(); void CategoryListDataReceived();
// Search a keyword (and switch pages)
void SearchKeyword(QString aKeyword, int aPage);
void SearchDataReceived();
private: private:
void PopulateAppList(QJsonObject appData); void PopulateAppList(QJsonObject appData, QString &&keyword);
}; };

@ -9,5 +9,7 @@
<qresource prefix="/"> <qresource prefix="/">
<file>icons/spark-store.svg</file> <file>icons/spark-store.svg</file>
<file>icons/settings-dark.svg</file> <file>icons/settings-dark.svg</file>
<file>icons/clear-input.svg</file>
<file>icons/search-mini.svg</file>
</qresource> </qresource>
</RCC> </RCC>