#ifndef SUGGESTER_H
#define SUGGESTER_H
-#include <QtGui>
-#if QT_VERSION >= 0x050000
-#include <QtWidgets>
-#endif
+#include <QtCore>
+
+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 = 0) : QObject(parent) { }
- virtual void suggest(QString query) = 0;
+ Suggester(QObject *parent) : QObject(parent) { }
+ virtual void suggest(const QString &query) = 0;
signals:
- void ready(QStringList);
+ void ready(const QList<Suggestion*> &suggestions);
};
#endif // SUGGESTER_H
+
/* $BEGIN_LICENSE
This file is part of Minitube.
-Copyright 2009, Flavio Tordini <flavio.tordini@gmail.com>
+Copyright 2013, Flavio Tordini <flavio.tordini@gmail.com>
Minitube is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with Minitube. If not, see <http://www.gnu.org/licenses/>.
$END_LICENSE */
-
#include "autocomplete.h"
#include "suggester.h"
#ifdef APP_MAC
#include "searchlineedit.h"
#endif
-AutoComplete::AutoComplete(SearchLineEdit *parent, QLineEdit *editor):
- QObject(parent), editor(editor), suggester(0) {
+AutoComplete::AutoComplete(SearchLineEdit *buddy, QLineEdit *lineEdit):
+ QObject(buddy), buddy(buddy), lineEdit(lineEdit), suggester(0) {
- buddy = parent;
enabled = true;
- popup = new QListWidget;
- popup->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ popup = new QListWidget();
popup->setMouseTracking(true);
- popup->setWindowOpacity(.9);
- popup->installEventFilter(this);
popup->setWindowFlags(Qt::Popup);
+ popup->setAttribute(Qt::WA_ShowWithoutActivating);
popup->setFocusPolicy(Qt::NoFocus);
popup->setFocusProxy(buddy);
+ popup->installEventFilter(this);
+ 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(doneCompletion()));
-
- // connect(popup, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
- // SLOT(currentItemChanged(QListWidgetItem *)));
-
- // mouse hover
- // connect(popup, SIGNAL(itemEntered(QListWidgetItem*)),
- // SLOT(currentItemChanged(QListWidgetItem *)));
+ connect(popup, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(acceptSuggestion()));
+ connect(popup, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)),
+ SLOT(currentItemChanged(QListWidgetItem*)));
+ connect(popup, SIGNAL(itemEntered(QListWidgetItem*)), SLOT(itemEntered(QListWidgetItem *)));
timer = new QTimer(this);
timer->setSingleShot(true);
- timer->setInterval(600);
- connect(timer, SIGNAL(timeout()), SLOT(autoSuggest()));
- connect(buddy, SIGNAL(textChanged(QString)), timer, SLOT(start()));
-
+ timer->setInterval(500);
+ connect(timer, SIGNAL(timeout()), SLOT(suggest()));
+ connect(buddy, SIGNAL(textEdited(QString)), timer, SLOT(start()));
}
AutoComplete::~AutoComplete() {
}
bool AutoComplete::eventFilter(QObject *obj, QEvent *ev) {
- if (obj != popup)
- return false;
+ if (obj != popup) return false;
- if (ev->type() == QEvent::FocusOut) {
- popup->hide();
- buddy->setFocus();
+ if (ev->type() == QEvent::Leave) {
+ popup->setCurrentItem(0);
+ popup->clearSelection();
+ if (!originalText.isEmpty()) buddy->setText(originalText);
return true;
}
- if (ev->type() == QEvent::MouseButtonPress) {
+ if (ev->type() == QEvent::FocusOut) {
popup->hide();
- buddy->setFocus();
buddy->setText(originalText);
+ buddy->setFocus();
return true;
}
if (ev->type() == QEvent::KeyPress) {
-
bool consumed = false;
-
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(ev);
- int key = keyEvent->key();
- // qDebug() << keyEvent->text();
- switch (key) {
+ // qWarning() << keyEvent->text();
+ switch (keyEvent->key()) {
case Qt::Key_Enter:
case Qt::Key_Return:
if (popup->currentItem()) {
- doneCompletion();
+ acceptSuggestion();
consumed = true;
} else {
buddy->setFocus();
- editor->event(ev);
+ lineEdit->event(ev);
popup->hide();
}
break;
case Qt::Key_Escape:
- buddy->setFocus();
- editor->setText(originalText);
popup->hide();
+ popup->clear();
+ buddy->setText(originalText);
+ buddy->setFocus();
consumed = true;
break;
case Qt::Key_Up:
+ if (popup->currentRow() == 0) {
+ popup->setCurrentItem(0);
+ popup->clearSelection();
+ buddy->setText(originalText);
+ buddy->setFocus();
+ consumed = true;
+ }
+ break;
+
case Qt::Key_Down:
case Qt::Key_Home:
case Qt::Key_End:
case Qt::Key_PageUp:
case Qt::Key_PageDown:
+ // qDebug() << key;
break;
default:
// qDebug() << keyEvent->text();
- buddy->setFocus();
- editor->event(ev);
- popup->hide();
+ lineEdit->event(ev);
+ consumed = true;
break;
}
return false;
}
-void AutoComplete::showCompletion(const QStringList &choices) {
-
- if (choices.isEmpty())
+void AutoComplete::showCompletion(const QList<Suggestion *> &suggestions) {
+ if (suggestions.isEmpty()) {
+ popup->clear();
+ popup->hide();
return;
-
+ }
popup->setUpdatesEnabled(false);
popup->clear();
- for (int i = 0; i < choices.count(); ++i) {
+ for (int i = 0; i < suggestions.count(); ++i) {
QListWidgetItem * item;
item = new QListWidgetItem(popup);
- item->setText(choices[i]);
+ Suggestion *s = suggestions[i];
+ item->setText(s->value);
+ if (!s->type.isEmpty())
+ item->setIcon(QIcon(":/images/" + s->type + ".png"));
}
popup->setCurrentItem(0);
- popup->adjustSize();
- popup->setUpdatesEnabled(true);
-
- int h = popup->sizeHintForRow(0) * choices.count() + 4;
+ int h = 0;
+ 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->setFrameShape(QFrame::NoFrame);
-
popup->setFocus();
- popup->show();
+
+ if (popup->isHidden()) popup->show();
+ popup->setUpdatesEnabled(true);
}
-void AutoComplete::doneCompletion() {
+void AutoComplete::acceptSuggestion() {
timer->stop();
+ originalText.clear();
popup->hide();
buddy->setFocus();
- QListWidgetItem *item = popup->currentItem();
- if (item) {
- buddy->setText(item->text());
- emit suggestionAccepted(item->text());
- }
+ 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();
+ } else qWarning() << "No suggestion for index" << index;
}
void AutoComplete::preventSuggest() {
- // qDebug() << "preventSuggest";
timer->stop();
enabled = false;
popup->hide();
}
void AutoComplete::enableSuggest() {
- // qDebug() << "enableSuggest";
enabled = true;
}
void AutoComplete::setSuggester(Suggester* suggester) {
if (this->suggester) this->suggester->disconnect();
this->suggester = suggester;
- connect(suggester, SIGNAL(ready(QStringList)), SLOT(suggestionsReady(QStringList)));
+ connect(suggester, SIGNAL(ready(QList<Suggestion*>)), SLOT(suggestionsReady(QList<Suggestion*>)));
}
-void AutoComplete::autoSuggest() {
+void AutoComplete::suggest() {
if (!enabled) return;
if (!buddy->hasFocus()) return;
- QString query = editor->text();
- originalText = query;
- // qDebug() << "originalText" << originalText;
- if (query.isEmpty()) {
+ popup->setCurrentItem(0);
+ popup->clearSelection();
+
+ originalText = buddy->text();
+ if (originalText.isEmpty()) {
popup->hide();
buddy->setFocus();
return;
}
- if (suggester)
- suggester->suggest(query);
+ if (suggester) suggester->suggest(originalText);
}
-void AutoComplete::suggestionsReady(QStringList suggestions) {
+void AutoComplete::suggestionsReady(const QList<Suggestion *> &suggestions) {
+ qDeleteAll(this->suggestions);
+ this->suggestions = suggestions;
if (!enabled) return;
+ if (!buddy->hasFocus()) return;
showCompletion(suggestions);
}
-void AutoComplete::currentItemChanged(QListWidgetItem *current) {
- if (current) {
- // qDebug() << "current" << current->text();
- current->setSelected(true);
- buddy->setText(current->text());
- editor->setSelection(originalText.length(), editor->text().length());
- }
+void AutoComplete::itemEntered(QListWidgetItem *item) {
+ if (!item) return;
+ item->setSelected(true);
+ popup->setCurrentItem(item);
+}
+
+void AutoComplete::currentItemChanged(QListWidgetItem *item) {
+ if (!item) return;
+ buddy->setText(item->text());
+ // lineEdit->setSelection(originalText.length(), editor->text().length());
}
along with Minitube. If not, see <http://www.gnu.org/licenses/>.
$END_LICENSE */
-
-#ifndef SUGGESTCOMPLETION_H
-#define SUGGESTCOMPLETION_H
+#ifndef AUTOCOMPLETE_H
+#define AUTOCOMPLETE_H
#include <QtGui>
-#if QT_VERSION >= 0x050000
-#include <QtWidgets>
-#endif
class Suggester;
+class Suggestion;
class SearchLineEdit;
class AutoComplete : public QObject {
+
Q_OBJECT
public:
- AutoComplete(SearchLineEdit *parent, QLineEdit *editor);
+ AutoComplete(SearchLineEdit *buddy, QLineEdit *lineEdit);
~AutoComplete();
bool eventFilter(QObject *obj, QEvent *ev);
- void showCompletion(const QStringList &choices);
+ void showCompletion(const QList<Suggestion*> &suggestions);
void setSuggester(Suggester* suggester);
QListWidget* getPopup() { return popup; }
public slots:
- void doneCompletion();
+ void acceptSuggestion();
void preventSuggest();
void enableSuggest();
- void autoSuggest();
- void currentItemChanged(QListWidgetItem *current);
- void suggestionsReady(QStringList suggestions);
+ void suggest();
+ void itemEntered(QListWidgetItem *item);
+ void currentItemChanged(QListWidgetItem *item);
+ void suggestionsReady(const QList<Suggestion*> &suggestions);
signals:
- void suggestionAccepted(const QString &suggestion);
+ void suggestionAccepted(Suggestion *suggestion);
+ void suggestionAccepted(const QString &value);
private:
SearchLineEdit *buddy;
- QLineEdit *editor;
+ QLineEdit *lineEdit;
QString originalText;
QListWidget *popup;
QTimer *timer;
bool enabled;
- Suggester* suggester;
+ Suggester *suggester;
+ QList<Suggestion*> suggestions;
};
-#endif // SUGGESTCOMPLETION_H
+#endif // AUTOCOMPLETE_H
}
-void ChannelSuggest::suggest(QString query) {
+void ChannelSuggest::suggest(const QString &query) {
QUrl url("http://www.youtube.com/results");
#if QT_VERSION >= 0x050000
{
void ChannelSuggest::handleNetworkData(QByteArray data) {
QStringList choices;
+ QList<Suggestion*> suggestions;
+
QString html = QString::fromUtf8(data);
QRegExp re("/user/([a-zA-Z0-9]+)");
// qDebug() << re.cap(0) << re.cap(1);
QString choice = re.cap(1);
if (!choices.contains(choice, Qt::CaseInsensitive)) {
+ suggestions << new Suggestion(choice);
choices << choice;
if (choices.size() == 10) break;
}
pos += re.matchedLength();
}
- emit ready(choices);
+ emit ready(suggestions);
}
public:
ChannelSuggest(QObject *parent = 0);
- void suggest(QString query);
+ void suggest(const QString &query);
signals:
- void ready(QStringList);
+ void ready(const QList<Suggestion*> &suggestions);
private slots:
void handleNetworkData(QByteArray response);
-/****************************************************************************
-**
-** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-** This file is part of the demonstration applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial Usage
-** Licensees holding valid Qt Commercial licenses may use this file in
-** accordance with the Qt Commercial License Agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Nokia.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain
-** additional rights. These rights are described in the Nokia Qt LGPL
-** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
-** package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-** If you are unsure which license is appropriate for your use, please
-** contact the sales department at http://www.qtsoftware.com/contact.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
#include "searchlineedit.h"
#include <QPainter>
SearchLineEdit::SearchLineEdit(QWidget *parent) : ExLineEdit(parent),
m_searchButton(new SearchButton(this))
{
- connect(lineEdit(), SIGNAL(textChanged(const QString &)),
- this, SIGNAL(textChanged(const QString &)));
-
- connect(lineEdit(), SIGNAL(returnPressed()),
- this, SLOT(returnPressed()));
+ connect(lineEdit(), SIGNAL(textChanged(const QString &)), SIGNAL(textChanged(const QString &)));
+ connect(lineEdit(), SIGNAL(textEdited(const QString &)), SIGNAL(textEdited(const QString &)));
+ connect(lineEdit(), SIGNAL(returnPressed()), SLOT(returnPressed()));
setLeftWidget(m_searchButton);
m_inactiveText = tr("Search");
// completion
completion = new AutoComplete(this, m_lineEdit);
- connect(completion, SIGNAL(suggestionAccepted(const QString &)), SIGNAL(suggestionAccepted(const QString &)));
-
+ connect(completion, SIGNAL(suggestionAccepted(Suggestion*)), SIGNAL(suggestionAccepted(Suggestion*)));
}
void SearchLineEdit::paintEvent(QPaintEvent *event)
-/****************************************************************************
-**
-** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-** This file is part of the demonstration applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial Usage
-** Licensees holding valid Qt Commercial licenses may use this file in
-** accordance with the Qt Commercial License Agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Nokia.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain
-** additional rights. These rights are described in the Nokia Qt LGPL
-** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
-** package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-** If you are unsure which license is appropriate for your use, please
-** contact the sales department at http://www.qtsoftware.com/contact.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
#ifndef SEARCHLINEEDIT_H
#define SEARCHLINEEDIT_H
signals:
void textChanged(const QString &text);
+ void textEdited(const QString &text);
void search(const QString &text);
void suggestionAccepted(const QString &suggestion);
void returnPressed();
private:
-
SearchButton *m_searchButton;
QString m_inactiveText;
-
AutoComplete *completion;
};
#ifndef SUGGESTER_H
#define SUGGESTER_H
-#include <QtGui>
-#if QT_VERSION >= 0x050000
-#include <QtWidgets>
-#endif
+#include <QtCore>
+
+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 = 0) : QObject(parent) { }
- virtual void suggest(QString query) = 0;
+ Suggester(QObject *parent) : QObject(parent) { }
+ virtual void suggest(const QString &query) = 0;
signals:
- void ready(QStringList);
+ void ready(const QList<Suggestion*> &suggestions);
};
#endif // SUGGESTER_H
+
}
-void YTSuggester::suggest(QString query) {
+void YTSuggester::suggest(const QString &query) {
if (query.startsWith("http")) return;
#if QT_VERSION >= 0x040800
}
void YTSuggester::handleNetworkData(QByteArray response) {
- QStringList choices;
-
+ QList<Suggestion*> suggestions;
QXmlStreamReader xml(response);
while (!xml.atEnd()) {
xml.readNext();
if (xml.tokenType() == QXmlStreamReader::StartElement) {
if (xml.name() == QLatin1String("suggestion")) {
QStringRef str = xml.attributes().value("data");
- choices << str.toString();
+ QString value = str.toString();
+ suggestions << new Suggestion(value);
}
}
}
- emit ready(choices);
+ emit ready(suggestions);
}
public:
YTSuggester(QObject *parent = 0);
- void suggest(QString query);
+ void suggest(const QString &query);
signals:
- void ready(QStringList);
+ void ready(const QList<Suggestion*> &suggestions);
private slots:
void handleNetworkData(QByteArray response);