From 533fde1d4eb12d73e4af216b39516f9daf469c6b Mon Sep 17 00:00:00 2001 From: Flavio Tordini Date: Mon, 13 Oct 2014 16:12:45 +0200 Subject: [PATCH] better search suggestions --- src/Suggester.h | 68 ----------------------------------- src/autocomplete.cpp | 85 ++++++++++++++++++++++++++------------------ src/autocomplete.h | 17 +++++---- src/mainwindow.cpp | 29 +++++++-------- src/mainwindow.h | 5 +-- src/mediaview.cpp | 5 +-- src/searchlineedit.h | 2 +- src/searchview.cpp | 9 +++-- src/searchview.h | 2 ++ 9 files changed, 88 insertions(+), 134 deletions(-) delete mode 100644 src/Suggester.h diff --git a/src/Suggester.h b/src/Suggester.h deleted file mode 100644 index cca7f5f..0000000 --- a/src/Suggester.h +++ /dev/null @@ -1,68 +0,0 @@ -/* $BEGIN_LICENSE - -This file is part of Minitube. -Copyright 2009, Flavio Tordini - -Minitube is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Minitube is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Minitube. If not, see . - -$END_LICENSE */ - -#ifndef SUGGESTER_H -#define SUGGESTER_H - -#include - -class Suggestion { - -public: - Suggestion(QString value = QString(), - QString type = QString()) : - value(value), type(type) { } - QString value; - QString type; - - bool operator==(const Suggestion &other) const { - return (value == other.value) && (type == other.type); - } - - bool operator!=(const Suggestion &other) const { - return !(*this == other); - } - - bool operator==(Suggestion *other) const { - qDebug() << "Comparing" << this << other; - return (value == other->value) && (type == other->type); - } - - bool operator!=(Suggestion *other) const { - return !(this == other); - } - -}; - -class Suggester : public QObject { - - Q_OBJECT - -public: - Suggester(QObject *parent) : QObject(parent) { } - virtual void suggest(const QString &query) = 0; - -signals: - void ready(const QList &suggestions); - -}; - -#endif // SUGGESTER_H - diff --git a/src/autocomplete.cpp b/src/autocomplete.cpp index e6e0d42..cb0773a 100644 --- a/src/autocomplete.cpp +++ b/src/autocomplete.cpp @@ -21,14 +21,13 @@ $END_LICENSE */ #include "suggester.h" #ifdef APP_MAC #include "searchlineedit_mac.h" +#include "macutils.h" #else #include "searchlineedit.h" #endif AutoComplete::AutoComplete(SearchLineEdit *buddy, QLineEdit *lineEdit): - QObject(buddy), buddy(buddy), lineEdit(lineEdit), suggester(0) { - - enabled = true; + QObject(buddy), buddy(buddy), lineEdit(lineEdit), enabled(true), suggester(0) { popup = new QListWidget(); popup->setMouseTracking(true); @@ -37,18 +36,17 @@ AutoComplete::AutoComplete(SearchLineEdit *buddy, QLineEdit *lineEdit): popup->setFocusPolicy(Qt::NoFocus); popup->setFocusProxy(buddy); popup->installEventFilter(this); + buddy->window()->installEventFilter(this); + // style popup->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); popup->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); popup->setWindowOpacity(.9); popup->setProperty("suggest", true); - popup->setFrameShape(QFrame::NoFrame); - popup->setAttribute(Qt::WA_TranslucentBackground); - popup->viewport()->setStyleSheet("border:0; border-radius:5px; background:palette(base)"); connect(popup, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(acceptSuggestion())); connect(popup, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), - SLOT(currentItemChanged(QListWidgetItem*))); + SLOT(currentItemChanged(QListWidgetItem*))); connect(popup, SIGNAL(itemEntered(QListWidgetItem*)), SLOT(itemEntered(QListWidgetItem *))); timer = new QTimer(this); @@ -63,7 +61,17 @@ AutoComplete::~AutoComplete() { } bool AutoComplete::eventFilter(QObject *obj, QEvent *ev) { - if (obj != popup) return false; + if (obj != popup) { + switch (ev->type()) { + case QEvent::Move: + case QEvent::Resize: + adjustPosition(); + break; + default: + break; + } + return false; + } if (ev->type() == QEvent::Leave) { popup->setCurrentItem(0); @@ -72,10 +80,8 @@ bool AutoComplete::eventFilter(QObject *obj, QEvent *ev) { return true; } - if (ev->type() == QEvent::FocusOut) { - popup->hide(); - buddy->setText(originalText); - buddy->setFocus(); + if (ev->type() == QEvent::FocusOut || ev->type() == QEvent::MouseButtonPress) { + hideSuggestions(); return true; } @@ -90,17 +96,13 @@ bool AutoComplete::eventFilter(QObject *obj, QEvent *ev) { acceptSuggestion(); consumed = true; } else { - buddy->setFocus(); lineEdit->event(ev); - popup->hide(); + hideSuggestions(); } break; case Qt::Key_Escape: - popup->hide(); - popup->clear(); - buddy->setText(originalText); - buddy->setFocus(); + hideSuggestions(); consumed = true; break; @@ -137,15 +139,13 @@ bool AutoComplete::eventFilter(QObject *obj, QEvent *ev) { void AutoComplete::showCompletion(const QList &suggestions) { if (suggestions.isEmpty()) { - popup->clear(); - popup->hide(); + hideSuggestions(); return; } popup->setUpdatesEnabled(false); popup->clear(); for (int i = 0; i < suggestions.count(); ++i) { - QListWidgetItem * item; - item = new QListWidgetItem(popup); + QListWidgetItem *item = new QListWidgetItem(popup); Suggestion *s = suggestions[i]; item->setText(s->value); if (!s->type.isEmpty()) @@ -156,25 +156,24 @@ void AutoComplete::showCompletion(const QList &suggestions) { for (int i = 0; i < suggestions.count(); ++i) h += popup->sizeHintForRow(i); popup->resize(buddy->width(), h); - popup->move(buddy->mapToGlobal(QPoint(0, buddy->height()))); - popup->setFocus(); - - if (popup->isHidden()) popup->show(); + adjustPosition(); popup->setUpdatesEnabled(true); + + if (popup->isHidden()) { + popup->show(); + popup->setFocus(); + } } void AutoComplete::acceptSuggestion() { - timer->stop(); - originalText.clear(); - popup->hide(); - buddy->setFocus(); int index = popup->currentIndex().row(); if (index >= 0 && index < suggestions.size()) { Suggestion* suggestion = suggestions.at(index); buddy->setText(suggestion->value); emit suggestionAccepted(suggestion); emit suggestionAccepted(suggestion->value); - popup->clear(); + originalText.clear(); + hideSuggestions(); } else qWarning() << "No suggestion for index" << index; } @@ -182,7 +181,6 @@ void AutoComplete::preventSuggest() { timer->stop(); enabled = false; popup->hide(); - popup->setFrameShape(QFrame::NoFrame); } void AutoComplete::enableSuggest() { @@ -197,15 +195,13 @@ void AutoComplete::setSuggester(Suggester* suggester) { void AutoComplete::suggest() { if (!enabled) return; - if (!buddy->hasFocus()) return; popup->setCurrentItem(0); popup->clearSelection(); originalText = buddy->text(); if (originalText.isEmpty()) { - popup->hide(); - buddy->setFocus(); + hideSuggestions(); return; } @@ -220,6 +216,25 @@ void AutoComplete::suggestionsReady(const QList &suggestions) { showCompletion(suggestions); } +void AutoComplete::adjustPosition() { + popup->move(buddy->mapToGlobal(QPoint(0, buddy->height()))); +} + +void AutoComplete::hideSuggestions() { +#ifdef APP_MAC + mac::fadeOutWindow(popup); +#else + popup->hide(); + popup->clear(); +#endif + if (!originalText.isEmpty()) { + buddy->setText(originalText); + originalText.clear(); + } + buddy->setFocus(); + timer->stop(); +} + void AutoComplete::itemEntered(QListWidgetItem *item) { if (!item) return; item->setSelected(true); diff --git a/src/autocomplete.h b/src/autocomplete.h index 6894d7e..1699ebe 100644 --- a/src/autocomplete.h +++ b/src/autocomplete.h @@ -37,21 +37,24 @@ public: void showCompletion(const QList &suggestions); void setSuggester(Suggester* suggester); QListWidget* getPopup() { return popup; } - -public slots: - void acceptSuggestion(); void preventSuggest(); void enableSuggest(); - void suggest(); - void itemEntered(QListWidgetItem *item); - void currentItemChanged(QListWidgetItem *item); - void suggestionsReady(const QList &suggestions); signals: void suggestionAccepted(Suggestion *suggestion); void suggestionAccepted(const QString &value); +private slots: + void acceptSuggestion(); + void suggest(); + void itemEntered(QListWidgetItem *item); + void currentItemChanged(QListWidgetItem *item); + void suggestionsReady(const QList &suggestions); + void adjustPosition(); + private: + void hideSuggestions(); + SearchLineEdit *buddy; QLineEdit *lineEdit; QString originalText; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 9ea7777..d7093d6 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -727,6 +727,7 @@ void MainWindow::createToolBars() { #ifdef APP_PHONON_SEEK seekSlider = new Phonon::SeekSlider(this); + seekSlider->setTracking(true); seekSlider->setIconVisible(false); seekSlider->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); mainToolBar->addWidget(seekSlider); @@ -774,8 +775,8 @@ void MainWindow::createToolBars() { #endif toolbarSearch->setMinimumWidth(toolbarSearch->fontInfo().pixelSize()*15); toolbarSearch->setSuggester(new YTSuggester(this)); - connect(toolbarSearch, SIGNAL(search(const QString&)), this, SLOT(startToolbarSearch(const QString&))); - connect(toolbarSearch, SIGNAL(suggestionAccepted(const QString&)), SLOT(startToolbarSearch(const QString&))); + connect(toolbarSearch, SIGNAL(search(const QString&)), SLOT(search(const QString&))); + connect(toolbarSearch, SIGNAL(suggestionAccepted(Suggestion*)), SLOT(suggestionAccepted(Suggestion*))); toolbarSearch->setStatusTip(searchFocusAct->statusTip()); #ifdef APP_MAC mainToolBar->addWidget(searchWrapper); @@ -1512,29 +1513,25 @@ void MainWindow::toggleDownloads(bool show) { else goBack(); } -void MainWindow::startToolbarSearch(QString query) { - query = query.trimmed(); - - // check for empty query - if (query.length() == 0) { - return; - } +void MainWindow::suggestionAccepted(Suggestion *suggestion) { + search(suggestion->value); +} +void MainWindow::search(const QString &query) { + QString q = query.trimmed(); + if (q.length() == 0) return; SearchParams *searchParams = new SearchParams(); - searchParams->setKeywords(query); - - // go! + searchParams->setKeywords(q); showMedia(searchParams); } void MainWindow::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasFormat("text/uri-list")) { QList urls = event->mimeData()->urls(); - if (urls.isEmpty()) - return; + if (urls.isEmpty()) return; QUrl url = urls.first(); QString videoId = YTSearch::videoIdFromUrl(url.toString()); - if (!videoId.isNull()) + if (!videoId.isEmpty()) event->acceptProposedAction(); } } @@ -1547,7 +1544,7 @@ void MainWindow::dropEvent(QDropEvent *event) { return; QUrl url = urls.first(); QString videoId = YTSearch::videoIdFromUrl(url.toString()); - if (!videoId.isNull()) { + if (!videoId.isEmpty()) { setWindowTitle(url.toString()); SearchParams *searchParams = new SearchParams(); searchParams->setKeywords(videoId); diff --git a/src/mainwindow.h b/src/mainwindow.h index be9ff14..1c76171 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -31,7 +31,6 @@ $END_LICENSE */ #include #include #endif -#include "view.h" class HomeView; class MediaView; @@ -40,6 +39,7 @@ class SearchLineEdit; class UpdateChecker; class SearchParams; class VideoSource; +class Suggestion; class MainWindow : public QMainWindow { @@ -73,7 +73,8 @@ public slots: void restore(); void messageReceived(const QString &message); void quit(); - void startToolbarSearch(QString query); + void suggestionAccepted(Suggestion *suggestion); + void search(const QString &query); void goBack(); void showMessage(QString message); #ifdef APP_ACTIVATION diff --git a/src/mediaview.cpp b/src/mediaview.cpp index 2279404..7b62b1e 100644 --- a/src/mediaview.cpp +++ b/src/mediaview.cpp @@ -102,7 +102,7 @@ void MediaView::initialize() { connect(playlistModel, SIGNAL(haveSuggestions(const QStringList &)), sidebar, SLOT(showSuggestions(const QStringList &))); connect(sidebar, SIGNAL(suggestionAccepted(QString)), - MainWindow::instance(), SLOT(startToolbarSearch(QString))); + MainWindow::instance(), SLOT(search(QString))); splitter->addWidget(sidebar); videoAreaWidget = new VideoAreaWidget(this); @@ -301,7 +301,8 @@ void MediaView::disappear() { } -void MediaView::handleError(QString /* message */) { +void MediaView::handleError(QString message) { + qWarning() << __PRETTY_FUNCTION__ << message; #ifdef APP_PHONON_SEEK mediaObject->play(); #else diff --git a/src/searchlineedit.h b/src/searchlineedit.h index 2124565..5f9fd85 100644 --- a/src/searchlineedit.h +++ b/src/searchlineedit.h @@ -41,7 +41,7 @@ signals: void textChanged(const QString &text); void textEdited(const QString &text); void search(const QString &text); - void suggestionAccepted(const QString &suggestion); + void suggestionAccepted(Suggestion *suggestion); public: SearchLineEdit(QWidget *parent = 0); diff --git a/src/searchview.cpp b/src/searchview.cpp index 77aa0df..42707d4 100644 --- a/src/searchview.cpp +++ b/src/searchview.cpp @@ -134,10 +134,9 @@ SearchView::SearchView(QWidget *parent) : QWidget(parent) { queryEdit = new SearchLineEdit(this); queryEdit->setFont(biggerFont); - queryEdit->setMinimumWidth(queryEdit->fontInfo().pixelSize()*15); connect(queryEdit, SIGNAL(search(const QString&)), SLOT(watch(const QString&))); - connect(queryEdit, SIGNAL(textChanged(const QString &)), SLOT(textChanged(const QString &))); - connect(queryEdit, SIGNAL(suggestionAccepted(const QString&)), SLOT(watch(const QString&))); + connect(queryEdit, SIGNAL(textEdited(const QString &)), SLOT(textChanged(const QString &))); + connect(queryEdit, SIGNAL(suggestionAccepted(Suggestion*)), SLOT(suggestionAccepted(Suggestion*))); youtubeSuggest = new YTSuggester(this); channelSuggest = new ChannelSuggest(this); @@ -398,3 +397,7 @@ void SearchView::searchTypeChanged(int index) { queryEdit->selectAll(); queryEdit->setFocus(); } + +void SearchView::suggestionAccepted(Suggestion *suggestion) { + watch(suggestion->value); +} diff --git a/src/searchview.h b/src/searchview.h index 490a9a2..f513c61 100644 --- a/src/searchview.h +++ b/src/searchview.h @@ -31,6 +31,7 @@ class SearchLineEdit; class SearchParams; class YTSuggester; class ChannelSuggest; +class Suggestion; class SearchView : public QWidget, public View { @@ -58,6 +59,7 @@ private slots: void watch(); void textChanged(const QString &text); void searchTypeChanged(int index); + void suggestionAccepted(Suggestion *suggestion); private: YTSuggester *youtubeSuggest; -- 2.39.5