Merge pull request #440 from kiwix/fluid-suggestionlist

Fluid suggestions list UX
This commit is contained in:
Matthieu Gautier 2020-06-30 16:52:30 +02:00 committed by GitHub
commit d7d43fcaf8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 138 additions and 45 deletions

View File

@ -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 \

View File

@ -1,10 +1,10 @@
#include "searchbar.h"
#include <QCompleter>
#include <QTimer>
#include <QFocusEvent>
#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<QUrl>>("QVector<QUrl>");
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<const QModelIndex &>::of(&QCompleter::activated),
this, &SearchBar::openCompletion);
this, QOverload<const QModelIndex &>::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<QUrl>& 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();
}

View File

@ -7,6 +7,8 @@
#include <QIcon>
#include <QPushButton>
#include <QUrl>
#include <QTimer>
#include <QThread>
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

View File

@ -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<QUrl> 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));
}

View File

@ -0,0 +1,23 @@
#ifndef SUGGESTIONLISTWORKER_H
#define SUGGESTIONLISTWORKER_H
#include <QUrl>
#include <QVector>
#include <QThread>
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<QUrl>& urlList, int token);
private:
QString m_text;
int m_token = 0;
};
#endif // SUGGESTIONLISTWORKER_H

View File

@ -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, [=]() {