Skip to content

Commit

Permalink
Add username autocompletion popup menu (#2866)
Browse files Browse the repository at this point in the history
  • Loading branch information
talneoran authored Jun 19, 2021
1 parent d21858b commit f605221
Show file tree
Hide file tree
Showing 12 changed files with 193 additions and 123 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unversioned

- Major: Added username autocompletion popup menu when typing usernames with an @ prefix. (#1979, #2866)
- Major: Added ability to toggle visibility of Channel Tabs - This can be done by right-clicking the tab area or pressing the keyboard shortcut (default: Ctrl+U). (#2600)
- Minor: Restore automod functionality for moderators (#2817, #2887)
- Minor: Add setting for username style (#2889, #2891)
Expand Down
8 changes: 4 additions & 4 deletions chatterino.pro
Original file line number Diff line number Diff line change
Expand Up @@ -318,8 +318,8 @@ SOURCES += \
src/widgets/settingspages/NotificationPage.cpp \
src/widgets/settingspages/SettingsPage.cpp \
src/widgets/splits/ClosedSplits.cpp \
src/widgets/splits/EmoteInputItem.cpp \
src/widgets/splits/EmoteInputPopup.cpp \
src/widgets/splits/InputCompletionItem.cpp \
src/widgets/splits/InputCompletionPopup.cpp \
src/widgets/splits/Split.cpp \
src/widgets/splits/SplitContainer.cpp \
src/widgets/splits/SplitHeader.cpp \
Expand Down Expand Up @@ -579,8 +579,8 @@ HEADERS += \
src/widgets/settingspages/NotificationPage.hpp \
src/widgets/settingspages/SettingsPage.hpp \
src/widgets/splits/ClosedSplits.hpp \
src/widgets/splits/EmoteInputItem.hpp \
src/widgets/splits/EmoteInputPopup.hpp \
src/widgets/splits/InputCompletionItem.hpp \
src/widgets/splits/InputCompletionPopup.hpp \
src/widgets/splits/Split.hpp \
src/widgets/splits/SplitContainer.hpp \
src/widgets/splits/SplitHeader.hpp \
Expand Down
8 changes: 4 additions & 4 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -437,10 +437,10 @@ set(SOURCE_FILES

widgets/splits/ClosedSplits.cpp
widgets/splits/ClosedSplits.hpp
widgets/splits/EmoteInputItem.cpp
widgets/splits/EmoteInputItem.hpp
widgets/splits/EmoteInputPopup.cpp
widgets/splits/EmoteInputPopup.hpp
widgets/splits/InputCompletionItem.cpp
widgets/splits/InputCompletionItem.hpp
widgets/splits/InputCompletionPopup.cpp
widgets/splits/InputCompletionPopup.hpp
widgets/splits/Split.cpp
widgets/splits/Split.hpp
widgets/splits/SplitContainer.cpp
Expand Down
2 changes: 2 additions & 0 deletions src/singletons/Settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ class Settings : public ABSettings, public ConcurrentSettings
"/behaviour/autocompletion/userCompletionOnlyWithAt", false};
BoolSetting emoteCompletionWithColon = {
"/behaviour/autocompletion/emoteCompletionWithColon", true};
BoolSetting showUsernameCompletionMenu = {
"/behaviour/autocompletion/showUsernameCompletionMenu", true};

FloatSetting pauseOnHoverDuration = {"/behaviour/pauseOnHoverDuration", 0};
EnumSetting<Qt::KeyboardModifier> pauseChatModifier = {
Expand Down
2 changes: 2 additions & 0 deletions src/widgets/settingspages/GeneralPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,8 @@ void GeneralPage::initLayout(GeneralPageView &layout)
layout.addCheckbox("Color @usernames", s.colorUsernames);
layout.addCheckbox("Try to find usernames without @ prefix",
s.findAllUsernames);
layout.addCheckbox("Show username autocompletion popup menu",
s.showUsernameCompletionMenu);
const QStringList usernameDisplayModes = {"Username", "Localized name",
"Username and localized name"};

Expand Down
63 changes: 0 additions & 63 deletions src/widgets/splits/EmoteInputItem.cpp

This file was deleted.

76 changes: 76 additions & 0 deletions src/widgets/splits/InputCompletionItem.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#include "InputCompletionItem.hpp"

namespace chatterino {

InputCompletionItem::InputCompletionItem(const EmotePtr &emote,
const QString &text,
ActionCallback action)
: emote_(emote)
, text_(text)
, action_(action)
{
}

void InputCompletionItem::action()
{
if (this->action_)
{
if (this->emote_)
this->action_(this->emote_->name.string);
else
this->action_(this->text_);
}
}

void InputCompletionItem::paint(QPainter *painter, const QRect &rect) const
{
auto margin = 4;
QRect textRect;
if (this->emote_)
{
painter->setRenderHint(QPainter::SmoothPixmapTransform);
painter->setRenderHint(QPainter::Antialiasing);

auto imageHeight = ICON_SIZE.height() - margin * 2;

QRect iconRect{
rect.topLeft() + QPoint{margin, margin},
QSize{imageHeight, imageHeight},
};

if (auto image = this->emote_->images.getImage(2))
{
if (auto pixmap = image->pixmapOrLoad())
{
if (image->height() != 0)
{
auto aspectRatio =
double(image->width()) / double(image->height());

iconRect = {
rect.topLeft() + QPoint{margin, margin},
QSize(int(imageHeight * aspectRatio), imageHeight)};
painter->drawPixmap(iconRect, *pixmap);
}
}
}

textRect =
QRect(iconRect.topRight() + QPoint{margin, 0},
QSize(rect.width() - iconRect.width(), iconRect.height()));
}
else
{
textRect = QRect(rect.topLeft() + QPoint{margin, 0},
QSize(rect.width(), rect.height()));
}

painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, this->text_);
}

QSize InputCompletionItem::sizeHint(const QRect &rect) const
{
return QSize(rect.width(), ICON_SIZE.height());
}

} // namespace chatterino
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@

namespace chatterino {

class EmoteInputItem : public GenericListItem
class InputCompletionItem : public GenericListItem
{
using ActionCallback = std::function<void(const QString &)>;

public:
EmoteInputItem(const EmotePtr &emote, const QString &text,
ActionCallback action);
InputCompletionItem(const EmotePtr &emote, const QString &text,
ActionCallback action);

// GenericListItem interface
public:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "EmoteInputPopup.hpp"
#include "InputCompletionPopup.hpp"

#include "Application.hpp"
#include "controllers/accounts/AccountController.hpp"
Expand All @@ -10,7 +10,7 @@
#include "singletons/Emotes.hpp"
#include "util/LayoutCreator.hpp"
#include "widgets/listview/GenericListView.hpp"
#include "widgets/splits/EmoteInputItem.hpp"
#include "widgets/splits/InputCompletionItem.hpp"

namespace chatterino {
namespace {
Expand Down Expand Up @@ -41,7 +41,7 @@ namespace {
}
} // namespace

EmoteInputPopup::EmoteInputPopup(QWidget *parent)
InputCompletionPopup::InputCompletionPopup(QWidget *parent)
: BasePopup({BasePopup::EnableCustomFrame, BasePopup::Frameless,
BasePopup::DontFocus},
parent)
Expand All @@ -56,7 +56,7 @@ EmoteInputPopup::EmoteInputPopup(QWidget *parent)
this->redrawTimer_.setInterval(33);
}

void EmoteInputPopup::initLayout()
void InputCompletionPopup::initLayout()
{
LayoutCreator creator = {this};

Expand All @@ -71,7 +71,7 @@ void EmoteInputPopup::initLayout()
});
}

void EmoteInputPopup::updateEmotes(const QString &text, ChannelPtr channel)
void InputCompletionPopup::updateEmotes(const QString &text, ChannelPtr channel)
{
std::vector<_Emote> emotes;
auto tc = dynamic_cast<TwitchChannel *>(channel.get());
Expand Down Expand Up @@ -122,11 +122,11 @@ void EmoteInputPopup::updateEmotes(const QString &text, ChannelPtr channel)
int count = 0;
for (auto &&emote : emotes)
{
this->model_.addItem(std::make_unique<EmoteInputItem>(
this->model_.addItem(std::make_unique<InputCompletionItem>(
emote.emote, emote.displayName + " - " + emote.providerName,
this->callback_));

if (count++ == maxEmoteCount)
if (count++ == maxEntryCount)
break;
}

Expand All @@ -136,22 +136,45 @@ void EmoteInputPopup::updateEmotes(const QString &text, ChannelPtr channel)
}
}

bool EmoteInputPopup::eventFilter(QObject *watched, QEvent *event)
void InputCompletionPopup::updateUsers(const QString &text, ChannelPtr channel)
{
auto twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
if (twitchChannel)
{
auto chatters = twitchChannel->accessChatters()->filterByPrefix(text);
this->model_.clear();
int count = 0;
for (const auto &name : chatters)
{
this->model_.addItem(std::make_unique<InputCompletionItem>(
nullptr, name, this->callback_));

if (count++ == maxEntryCount)
break;
}
if (!chatters.empty())
{
this->ui_.listView->setCurrentIndex(this->model_.index(0));
}
}
}

bool InputCompletionPopup::eventFilter(QObject *watched, QEvent *event)
{
return this->ui_.listView->eventFilter(watched, event);
}

void EmoteInputPopup::setInputAction(ActionCallback callback)
void InputCompletionPopup::setInputAction(ActionCallback callback)
{
this->callback_ = std::move(callback);
}

void EmoteInputPopup::showEvent(QShowEvent *)
void InputCompletionPopup::showEvent(QShowEvent *)
{
this->redrawTimer_.start();
}

void EmoteInputPopup::hideEvent(QHideEvent *)
void InputCompletionPopup::hideEvent(QHideEvent *)
{
this->redrawTimer_.stop();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,17 @@ namespace chatterino {

class GenericListView;

class EmoteInputPopup : public BasePopup
class InputCompletionPopup : public BasePopup
{
using ActionCallback = std::function<void(const QString &)>;

constexpr static int maxEmoteCount = 200;
constexpr static int maxEntryCount = 200;

public:
EmoteInputPopup(QWidget *parent = nullptr);
InputCompletionPopup(QWidget *parent = nullptr);

void updateEmotes(const QString &text, ChannelPtr channel);
void updateUsers(const QString &text, ChannelPtr channel);
virtual bool eventFilter(QObject *, QEvent *event) override;

void setInputAction(ActionCallback callback);
Expand Down
Loading

0 comments on commit f605221

Please sign in to comment.