diff --git a/kiwix-desktop.pro b/kiwix-desktop.pro index 4aa3d58..191cefb 100644 --- a/kiwix-desktop.pro +++ b/kiwix-desktop.pro @@ -43,6 +43,7 @@ DEFINES += QT_DEPRECATED_WARNINGS SOURCES += \ src/contenttypefilter.cpp \ src/findinpagebar.cpp \ + src/suggestionlistworker.cpp \ src/translation.cpp \ src/main.cpp \ src/mainwindow.cpp \ @@ -75,6 +76,7 @@ SOURCES += \ HEADERS += \ src/contenttypefilter.h \ src/findinpagebar.h \ + src/suggestionlistworker.h \ src/translation.h \ src/mainwindow.h \ src/kiwixapp.h \ diff --git a/src/searchbar.cpp b/src/searchbar.cpp index e535f18..eca5c6d 100644 --- a/src/searchbar.cpp +++ b/src/searchbar.cpp @@ -1,10 +1,10 @@ #include "searchbar.h" #include -#include #include #include "kiwixapp.h" +#include "suggestionlistworker.h" SearchButton::SearchButton(QWidget *parent) : QPushButton(parent), @@ -62,6 +62,8 @@ SearchBar::SearchBar(QWidget *parent) : m_completer(&m_completionModel, this), m_button(this) { + mp_typingTimer = new QTimer(this); + mp_typingTimer->setSingleShot(true); setPlaceholderText(gt("search")); m_completer.setCompletionMode(QCompleter::UnfilteredPopupCompletion); m_completer.setCaseSensitivity(Qt::CaseInsensitive); @@ -75,13 +77,15 @@ SearchBar::SearchBar(QWidget *parent) : QString style(byteContent); m_completer.popup()->setStyleSheet(style); - connect(this, &QLineEdit::textEdited, this, &SearchBar::updateCompletion); + qRegisterMetaType>("QVector"); + connect(mp_typingTimer, &QTimer::timeout, this, &SearchBar::updateCompletion); connect(KiwixApp::instance(), &KiwixApp::currentTitleChanged, this, &SearchBar::on_currentTitleChanged); connect(this, &QLineEdit::textEdited, this, [=](const QString &text) { m_searchbarInput = text; m_returnPressed = false; + mp_typingTimer->start(100); }); connect(this, &QLineEdit::textChanged, this, [=](const QString &text) { @@ -94,6 +98,18 @@ SearchBar::SearchBar(QWidget *parent) : }); } +void SearchBar::hideSuggestions() +{ + m_completer.popup()->hide(); +} + +void SearchBar::clearSuggestions() +{ + QStringList empty; + m_completionModel.setStringList(empty); + m_urlList.clear(); +} + void SearchBar::on_currentTitleChanged(const QString& title) { if (this->hasFocus()) { @@ -117,7 +133,7 @@ void SearchBar::focusInEvent( QFocusEvent* event) if (event->reason() == Qt::ActiveWindowFocusReason || event->reason() == Qt::MouseFocusReason) { connect(&m_completer, QOverload::of(&QCompleter::activated), - this, &SearchBar::openCompletion); + this, QOverload::of(&SearchBar::openCompletion)); } QLineEdit::focusInEvent(event); m_button.set_searchMode(true); @@ -134,56 +150,46 @@ void SearchBar::focusOutEvent(QFocusEvent* event) return QLineEdit::focusOutEvent(event); } -void SearchBar::updateCompletion(const QString &text) +void SearchBar::updateCompletion() { - QStringList wordList; - m_urlList.clear(); + mp_typingTimer->stop(); + clearSuggestions(); auto currentWidget = KiwixApp::instance()->getTabWidget()->currentWebView(); - if (!currentWidget) { - m_completionModel.setStringList(wordList); + if (!currentWidget || currentWidget->url().isEmpty() || m_searchbarInput.isEmpty()) { + hideSuggestions(); return; } - auto qurl = currentWidget->url(); - qInfo() << "Search bar url is " << qurl; - auto currentZimId = qurl.host().split(".")[0]; - auto reader = KiwixApp::instance()->getLibrary()->getReader(currentZimId); - QUrl url; - url.setScheme("zim"); - if (reader) { - url.setHost(currentZimId + ".zim"); - reader->searchSuggestionsSmart(text.toStdString(), 15); - std::string title, path; - while (reader->getNextSuggestion(title, path)) { - url.setPath(QString::fromStdString(path)); - wordList << QString::fromStdString(title); - m_urlList.push_back(url); + m_token++; + auto suggestionWorker = new SuggestionListWorker(m_searchbarInput, m_token, this); + connect(suggestionWorker, &SuggestionListWorker::searchFinished, this, + [=] (const QStringList& suggestions, const QVector& urlList, int token) { + if (token != m_token) { + return; } - } - QUrlQuery query; - url.setPath(""); - if (reader) { - // The host is used to determine the currentZimId - // The content query item is used to know in which zim search (as for kiwix-serve) - url.setHost(currentZimId + ".search"); - query.addQueryItem("content", currentZimId); - } else { - // We do not allow multi zim search for now. - // We don't have a correct UI to select on which zim search, - // how to display results, ... - //url.setHost("library.search"); - } - query.addQueryItem("pattern", text); - url.setQuery(query); - wordList << text + " (" + gt("fulltext-search") + ")"; - m_urlList.push_back(url); - m_completionModel.setStringList(wordList); + m_urlList = urlList; + if (m_returnPressed) { + openCompletion(suggestions.first(), 0); + return; + } + m_completionModel.setStringList(suggestions); + m_completer.complete(); + }); + connect(suggestionWorker, &SuggestionListWorker::finished, suggestionWorker, &QObject::deleteLater); + suggestionWorker->start(); } void SearchBar::openCompletion(const QModelIndex &index) +{ + if (m_urlList.size() != 0) { + openCompletion(index.data().toString(), index.row()); + } +} + +void SearchBar::openCompletion(const QString& text, int index) { QUrl url; - if (this->text().compare(index.data().toString(), Qt::CaseInsensitive) == 0) { - url = m_urlList.at(index.row()); + if (this->text().compare(text, Qt::CaseInsensitive) == 0) { + url = m_urlList.at(index); } else { url = m_urlList.last(); } diff --git a/src/searchbar.h b/src/searchbar.h index 1ed9773..378200a 100644 --- a/src/searchbar.h +++ b/src/searchbar.h @@ -7,6 +7,8 @@ #include #include #include +#include +#include class SearchButton : public QPushButton { Q_OBJECT @@ -26,9 +28,12 @@ class SearchBar : public QLineEdit Q_OBJECT public: SearchBar(QWidget *parent = nullptr); + void hideSuggestions(); public slots: void on_currentTitleChanged(const QString &title); + void clearSuggestions(); + protected: virtual void focusInEvent(QFocusEvent *); virtual void focusOutEvent(QFocusEvent *); @@ -40,10 +45,13 @@ private: QString m_title; QString m_searchbarInput; bool m_returnPressed = false; + QTimer* mp_typingTimer; + int m_token; private slots: - void updateCompletion(const QString& text); + void updateCompletion(); void openCompletion(const QModelIndex& index); + void openCompletion(const QString& text, int index); }; #endif // SEARCHBAR_H diff --git a/src/suggestionlistworker.cpp b/src/suggestionlistworker.cpp new file mode 100644 index 0000000..4f8bce4 --- /dev/null +++ b/src/suggestionlistworker.cpp @@ -0,0 +1,50 @@ +#include "suggestionlistworker.h" +#include "kiwixapp.h" + +SuggestionListWorker::SuggestionListWorker(const QString& text, int token, QObject *parent) +: QThread(parent), + m_text(text), + m_token(token) +{ +} + +void SuggestionListWorker::run() +{ + QStringList suggestionList; + QVector urlList; + + auto currentWidget = KiwixApp::instance()->getTabWidget()->currentWebView(); + auto qurl = currentWidget->url(); + auto currentZimId = qurl.host().split(".")[0]; + auto reader = KiwixApp::instance()->getLibrary()->getReader(currentZimId); + QUrl url; + url.setScheme("zim"); + if (reader) { + url.setHost(currentZimId + ".zim"); + reader->searchSuggestionsSmart(m_text.toStdString(), 15); + std::string title, path; + while (reader->getNextSuggestion(title, path)) { + url.setPath(QString::fromStdString(path)); + suggestionList.append(QString::fromStdString(title)); + urlList.append(url); + } + } + QUrlQuery query; + url.setPath(""); + if (reader) { + // The host is used to determine the currentZimId + // The content query item is used to know in which zim search (as for kiwix-serve) + url.setHost(currentZimId + ".search"); + query.addQueryItem("content", currentZimId); + } else { + // We do not allow multi zim search for now. + // We don't have a correct UI to select on which zim search, + // how to display results, ... + //url.setHost("library.search"); + } + query.addQueryItem("pattern", m_text); + url.setQuery(query); + suggestionList.append(m_text + " (" + gt("fulltext-search") + ")"); + urlList.append(url); + emit(searchFinished(suggestionList, urlList, m_token)); +} \ No newline at end of file diff --git a/src/suggestionlistworker.h b/src/suggestionlistworker.h new file mode 100644 index 0000000..a0e0566 --- /dev/null +++ b/src/suggestionlistworker.h @@ -0,0 +1,23 @@ +#ifndef SUGGESTIONLISTWORKER_H +#define SUGGESTIONLISTWORKER_H + +#include +#include +#include + +class SuggestionListWorker : public QThread +{ + Q_OBJECT +public: + SuggestionListWorker(const QString& text, int token, QObject *parent = nullptr); + void run() override; + +signals: + void searchFinished(const QStringList& suggestions, const QVector& urlList, int token); + +private: + QString m_text; + int m_token = 0; +}; + +#endif // SUGGESTIONLISTWORKER_H diff --git a/src/tabbar.cpp b/src/tabbar.cpp index f3ec3d1..7c635ea 100644 --- a/src/tabbar.cpp +++ b/src/tabbar.cpp @@ -27,7 +27,11 @@ TabBar::TabBar(QWidget *parent) : connect(app->getAction(KiwixApp::NewTabAction), &QAction::triggered, this, [=]() { this->createNewTab(true); - KiwixApp::instance()->getMainWindow()->getTopWidget()->getSearchBar().setFocus(Qt::MouseFocusReason); + auto topWidget = KiwixApp::instance()->getMainWindow()->getTopWidget(); + topWidget->getSearchBar().setFocus(Qt::MouseFocusReason); + topWidget->getSearchBar().clear(); + topWidget->getSearchBar().clearSuggestions(); + topWidget->getSearchBar().hideSuggestions(); }); connect(app->getAction(KiwixApp::CloseTabAction), &QAction::triggered, this, [=]() {