From 0b3029b3c48af4bf8aa302c28e66bce771213c94 Mon Sep 17 00:00:00 2001 From: christarazi Date: Tue, 10 Apr 2018 01:47:23 -0700 Subject: [PATCH] Implement pressing tab to navigate auto completion (#294) Fixes #287 * Fix pop-up not showing when less than max * Select suggestion by pressing Enter --- include/SuggestionsPopup.hpp | 13 +++++++++++ include/TextInputWidget.h | 2 ++ src/SuggestionsPopup.cpp | 42 ++++++++++++++++++++++++++++++++++-- src/TextInputWidget.cc | 19 +++++++++++++++- 4 files changed, 73 insertions(+), 3 deletions(-) diff --git a/include/SuggestionsPopup.hpp b/include/SuggestionsPopup.hpp index 23549124..80f28466 100644 --- a/include/SuggestionsPopup.hpp +++ b/include/SuggestionsPopup.hpp @@ -21,13 +21,18 @@ class PopupItem : public QWidget Q_OBJECT Q_PROPERTY(QColor hoverColor READ hoverColor WRITE setHoverColor) + Q_PROPERTY(bool hovering READ hovering WRITE setHovering) public: PopupItem(QWidget *parent, const QString &user_id); + QString user() const { return user_id_; } QColor hoverColor() const { return hoverColor_; } void setHoverColor(QColor &color) { hoverColor_ = color; } + bool hovering() const { return hovering_; } + void setHovering(const bool hover) { hovering_ = hover; }; + protected: void paintEvent(QPaintEvent *event) override; void mousePressEvent(QMouseEvent *event) override; @@ -43,6 +48,9 @@ private: QString user_id_; QColor hoverColor_; + + //! Set if the item is currently being hovered during tab completion (cycling). + bool hovering_; }; class SuggestionsPopup : public QWidget @@ -54,10 +62,15 @@ public: public slots: void addUsers(const QVector &users); + void cycleThroughSuggestions(); + void selectHoveredSuggestion(); signals: void itemSelected(const QString &user); private: QVBoxLayout *layout_; + + //! Counter for tab completion (cycling). + int tab_clicks_; }; diff --git a/include/TextInputWidget.h b/include/TextInputWidget.h index 927187dd..89eb5947 100644 --- a/include/TextInputWidget.h +++ b/include/TextInputWidget.h @@ -73,6 +73,8 @@ signals: //! Trigger the suggestion popup. void showSuggestions(const QString &query); void resultsRetrieved(const QVector &results); + void cycleSuggestions(); + void selectHoveredSuggestion(); public slots: void showResults(const QVector &results); diff --git a/src/SuggestionsPopup.cpp b/src/SuggestionsPopup.cpp index 8a900790..174076f9 100644 --- a/src/SuggestionsPopup.cpp +++ b/src/SuggestionsPopup.cpp @@ -1,8 +1,8 @@ +#include "SuggestionsPopup.hpp" #include "Avatar.h" #include "AvatarProvider.h" #include "Config.h" #include "DropShadow.h" -#include "SuggestionsPopup.hpp" #include "Utils.h" #include "timeline/TimelineViewManager.h" @@ -18,6 +18,7 @@ PopupItem::PopupItem(QWidget *parent, const QString &user_id) : QWidget(parent) , avatar_{new Avatar(this)} , user_id_{user_id} + , hovering_{false} { setMouseTracking(true); setAttribute(Qt::WA_Hover); @@ -56,7 +57,7 @@ PopupItem::paintEvent(QPaintEvent *) QPainter p(this); style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); - if (underMouse()) + if (underMouse() || hovering_) p.fillRect(rect(), hoverColor_); } @@ -71,6 +72,7 @@ PopupItem::mousePressEvent(QMouseEvent *event) SuggestionsPopup::SuggestionsPopup(QWidget *parent) : QWidget(parent) + , tab_clicks_(0) { setAttribute(Qt::WA_ShowWithoutActivating, true); setWindowFlags(Qt::ToolTip | Qt::NoDropShadowWindowHint); @@ -101,5 +103,41 @@ SuggestionsPopup::addUsers(const QVector &users) connect(user, &PopupItem::clicked, this, &SuggestionsPopup::itemSelected); } + tab_clicks_ = 0; // Reset to start from the beginning of pop-up window on next invocation. + resize(geometry().width(), 40 * users.size()); } + +void +SuggestionsPopup::cycleThroughSuggestions() +{ + tab_clicks_ %= layout_->count(); // Stay within the number of items in layout. + + // Reset flag for hovering effect first. + for (int i = 0; i < layout_->count(); ++i) { + const auto &p = qobject_cast(layout_->itemAt(i)->widget()); + p->setHovering(false); + } + + const auto &item = layout_->itemAt(tab_clicks_); + const auto &widget = qobject_cast(item->widget()); + widget->setHovering(true); + + ++tab_clicks_; + + update(); // Request to update the paint event. +} + +void +SuggestionsPopup::selectHoveredSuggestion() +{ + // Each tab press increments the counter by one, so the element desired is one off. + const auto item = layout_->itemAt(tab_clicks_ - 1); + if (!item) + return; + + const auto &widget = qobject_cast(item->widget()); + emit itemSelected(TimelineViewManager::displayName(widget->user())); + + tab_clicks_ = 0; // Reset to start from the beginning of pop-up window on next invocation. +} diff --git a/src/TextInputWidget.cc b/src/TextInputWidget.cc index fde4a061..459fcd51 100644 --- a/src/TextInputWidget.cc +++ b/src/TextInputWidget.cc @@ -86,6 +86,16 @@ FilteredTextEdit::FilteredTextEdit(QWidget *parent) cursor.insertText(text); }); + // For cycling through the suggestions by hitting tab. + connect(this, + &FilteredTextEdit::cycleSuggestions, + &popup_, + &SuggestionsPopup::cycleThroughSuggestions); + connect(this, + &FilteredTextEdit::selectHoveredSuggestion, + &popup_, + &SuggestionsPopup::selectHoveredSuggestion); + previewDialog_.hide(); } @@ -128,10 +138,15 @@ FilteredTextEdit::keyPressEvent(QKeyEvent *event) if (popup_.isVisible()) { switch (event->key()) { + case Qt::Key_Tab: + emit cycleSuggestions(); + return; case Qt::Key_Enter: case Qt::Key_Return: + emit selectHoveredSuggestion(); + return; case Qt::Key_Escape: - case Qt::Key_Tab: + break; case Qt::Key_Space: case Qt::Key_Backtab: { closeSuggestions(); @@ -464,6 +479,8 @@ TextInputWidget::TextInputWidget(QWidget *parent) if (items.size() >= MaxPopupItems) std::advance(end, MaxPopupItems); + else if (items.size() > 0) + std::advance(end, items.size()); for (auto it = items.begin(); it != end; it++) { const auto user = it->second;