Add username autocompletion popup menu (#2866)
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
## Unversioned
|
## 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)
|
- 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: Restore automod functionality for moderators (#2817, #2887)
|
||||||
- Minor: Add setting for username style (#2889, #2891)
|
- Minor: Add setting for username style (#2889, #2891)
|
||||||
|
|||||||
+4
-4
@@ -318,8 +318,8 @@ SOURCES += \
|
|||||||
src/widgets/settingspages/NotificationPage.cpp \
|
src/widgets/settingspages/NotificationPage.cpp \
|
||||||
src/widgets/settingspages/SettingsPage.cpp \
|
src/widgets/settingspages/SettingsPage.cpp \
|
||||||
src/widgets/splits/ClosedSplits.cpp \
|
src/widgets/splits/ClosedSplits.cpp \
|
||||||
src/widgets/splits/EmoteInputItem.cpp \
|
src/widgets/splits/InputCompletionItem.cpp \
|
||||||
src/widgets/splits/EmoteInputPopup.cpp \
|
src/widgets/splits/InputCompletionPopup.cpp \
|
||||||
src/widgets/splits/Split.cpp \
|
src/widgets/splits/Split.cpp \
|
||||||
src/widgets/splits/SplitContainer.cpp \
|
src/widgets/splits/SplitContainer.cpp \
|
||||||
src/widgets/splits/SplitHeader.cpp \
|
src/widgets/splits/SplitHeader.cpp \
|
||||||
@@ -579,8 +579,8 @@ HEADERS += \
|
|||||||
src/widgets/settingspages/NotificationPage.hpp \
|
src/widgets/settingspages/NotificationPage.hpp \
|
||||||
src/widgets/settingspages/SettingsPage.hpp \
|
src/widgets/settingspages/SettingsPage.hpp \
|
||||||
src/widgets/splits/ClosedSplits.hpp \
|
src/widgets/splits/ClosedSplits.hpp \
|
||||||
src/widgets/splits/EmoteInputItem.hpp \
|
src/widgets/splits/InputCompletionItem.hpp \
|
||||||
src/widgets/splits/EmoteInputPopup.hpp \
|
src/widgets/splits/InputCompletionPopup.hpp \
|
||||||
src/widgets/splits/Split.hpp \
|
src/widgets/splits/Split.hpp \
|
||||||
src/widgets/splits/SplitContainer.hpp \
|
src/widgets/splits/SplitContainer.hpp \
|
||||||
src/widgets/splits/SplitHeader.hpp \
|
src/widgets/splits/SplitHeader.hpp \
|
||||||
|
|||||||
+4
-4
@@ -437,10 +437,10 @@ set(SOURCE_FILES
|
|||||||
|
|
||||||
widgets/splits/ClosedSplits.cpp
|
widgets/splits/ClosedSplits.cpp
|
||||||
widgets/splits/ClosedSplits.hpp
|
widgets/splits/ClosedSplits.hpp
|
||||||
widgets/splits/EmoteInputItem.cpp
|
widgets/splits/InputCompletionItem.cpp
|
||||||
widgets/splits/EmoteInputItem.hpp
|
widgets/splits/InputCompletionItem.hpp
|
||||||
widgets/splits/EmoteInputPopup.cpp
|
widgets/splits/InputCompletionPopup.cpp
|
||||||
widgets/splits/EmoteInputPopup.hpp
|
widgets/splits/InputCompletionPopup.hpp
|
||||||
widgets/splits/Split.cpp
|
widgets/splits/Split.cpp
|
||||||
widgets/splits/Split.hpp
|
widgets/splits/Split.hpp
|
||||||
widgets/splits/SplitContainer.cpp
|
widgets/splits/SplitContainer.cpp
|
||||||
|
|||||||
@@ -171,6 +171,8 @@ public:
|
|||||||
"/behaviour/autocompletion/userCompletionOnlyWithAt", false};
|
"/behaviour/autocompletion/userCompletionOnlyWithAt", false};
|
||||||
BoolSetting emoteCompletionWithColon = {
|
BoolSetting emoteCompletionWithColon = {
|
||||||
"/behaviour/autocompletion/emoteCompletionWithColon", true};
|
"/behaviour/autocompletion/emoteCompletionWithColon", true};
|
||||||
|
BoolSetting showUsernameCompletionMenu = {
|
||||||
|
"/behaviour/autocompletion/showUsernameCompletionMenu", true};
|
||||||
|
|
||||||
FloatSetting pauseOnHoverDuration = {"/behaviour/pauseOnHoverDuration", 0};
|
FloatSetting pauseOnHoverDuration = {"/behaviour/pauseOnHoverDuration", 0};
|
||||||
EnumSetting<Qt::KeyboardModifier> pauseChatModifier = {
|
EnumSetting<Qt::KeyboardModifier> pauseChatModifier = {
|
||||||
|
|||||||
@@ -612,6 +612,8 @@ void GeneralPage::initLayout(GeneralPageView &layout)
|
|||||||
layout.addCheckbox("Color @usernames", s.colorUsernames);
|
layout.addCheckbox("Color @usernames", s.colorUsernames);
|
||||||
layout.addCheckbox("Try to find usernames without @ prefix",
|
layout.addCheckbox("Try to find usernames without @ prefix",
|
||||||
s.findAllUsernames);
|
s.findAllUsernames);
|
||||||
|
layout.addCheckbox("Show username autocompletion popup menu",
|
||||||
|
s.showUsernameCompletionMenu);
|
||||||
const QStringList usernameDisplayModes = {"Username", "Localized name",
|
const QStringList usernameDisplayModes = {"Username", "Localized name",
|
||||||
"Username and localized name"};
|
"Username and localized name"};
|
||||||
|
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
#include "EmoteInputItem.hpp"
|
|
||||||
|
|
||||||
namespace chatterino {
|
|
||||||
|
|
||||||
EmoteInputItem::EmoteInputItem(const EmotePtr &emote, const QString &text,
|
|
||||||
ActionCallback action)
|
|
||||||
: emote_(emote)
|
|
||||||
, text_(text)
|
|
||||||
, action_(action)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmoteInputItem::action()
|
|
||||||
{
|
|
||||||
if (this->action_ && this->emote_)
|
|
||||||
this->action_(this->emote_->name.string);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmoteInputItem::paint(QPainter *painter, const QRect &rect) const
|
|
||||||
{
|
|
||||||
painter->setRenderHint(QPainter::SmoothPixmapTransform);
|
|
||||||
painter->setRenderHint(QPainter::Antialiasing);
|
|
||||||
|
|
||||||
auto margin = 4;
|
|
||||||
auto imageHeight = ICON_SIZE.height() - margin * 2;
|
|
||||||
|
|
||||||
QRect iconRect{
|
|
||||||
rect.topLeft() + QPoint{margin, margin},
|
|
||||||
QSize{imageHeight, imageHeight},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this->emote_)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QRect textRect =
|
|
||||||
QRect(iconRect.topRight() + QPoint{margin, 0},
|
|
||||||
QSize(rect.width() - iconRect.width(), iconRect.height()));
|
|
||||||
painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, this->text_);
|
|
||||||
}
|
|
||||||
|
|
||||||
QSize EmoteInputItem::sizeHint(const QRect &rect) const
|
|
||||||
{
|
|
||||||
return QSize(rect.width(), ICON_SIZE.height());
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace chatterino
|
|
||||||
@@ -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
|
||||||
@@ -6,13 +6,13 @@
|
|||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
class EmoteInputItem : public GenericListItem
|
class InputCompletionItem : public GenericListItem
|
||||||
{
|
{
|
||||||
using ActionCallback = std::function<void(const QString &)>;
|
using ActionCallback = std::function<void(const QString &)>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
EmoteInputItem(const EmotePtr &emote, const QString &text,
|
InputCompletionItem(const EmotePtr &emote, const QString &text,
|
||||||
ActionCallback action);
|
ActionCallback action);
|
||||||
|
|
||||||
// GenericListItem interface
|
// GenericListItem interface
|
||||||
public:
|
public:
|
||||||
+34
-11
@@ -1,4 +1,4 @@
|
|||||||
#include "EmoteInputPopup.hpp"
|
#include "InputCompletionPopup.hpp"
|
||||||
|
|
||||||
#include "Application.hpp"
|
#include "Application.hpp"
|
||||||
#include "controllers/accounts/AccountController.hpp"
|
#include "controllers/accounts/AccountController.hpp"
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
#include "singletons/Emotes.hpp"
|
#include "singletons/Emotes.hpp"
|
||||||
#include "util/LayoutCreator.hpp"
|
#include "util/LayoutCreator.hpp"
|
||||||
#include "widgets/listview/GenericListView.hpp"
|
#include "widgets/listview/GenericListView.hpp"
|
||||||
#include "widgets/splits/EmoteInputItem.hpp"
|
#include "widgets/splits/InputCompletionItem.hpp"
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace {
|
namespace {
|
||||||
@@ -41,7 +41,7 @@ namespace {
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
EmoteInputPopup::EmoteInputPopup(QWidget *parent)
|
InputCompletionPopup::InputCompletionPopup(QWidget *parent)
|
||||||
: BasePopup({BasePopup::EnableCustomFrame, BasePopup::Frameless,
|
: BasePopup({BasePopup::EnableCustomFrame, BasePopup::Frameless,
|
||||||
BasePopup::DontFocus},
|
BasePopup::DontFocus},
|
||||||
parent)
|
parent)
|
||||||
@@ -56,7 +56,7 @@ EmoteInputPopup::EmoteInputPopup(QWidget *parent)
|
|||||||
this->redrawTimer_.setInterval(33);
|
this->redrawTimer_.setInterval(33);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmoteInputPopup::initLayout()
|
void InputCompletionPopup::initLayout()
|
||||||
{
|
{
|
||||||
LayoutCreator creator = {this};
|
LayoutCreator creator = {this};
|
||||||
|
|
||||||
@@ -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;
|
std::vector<_Emote> emotes;
|
||||||
auto tc = dynamic_cast<TwitchChannel *>(channel.get());
|
auto tc = dynamic_cast<TwitchChannel *>(channel.get());
|
||||||
@@ -122,11 +122,11 @@ void EmoteInputPopup::updateEmotes(const QString &text, ChannelPtr channel)
|
|||||||
int count = 0;
|
int count = 0;
|
||||||
for (auto &&emote : emotes)
|
for (auto &&emote : emotes)
|
||||||
{
|
{
|
||||||
this->model_.addItem(std::make_unique<EmoteInputItem>(
|
this->model_.addItem(std::make_unique<InputCompletionItem>(
|
||||||
emote.emote, emote.displayName + " - " + emote.providerName,
|
emote.emote, emote.displayName + " - " + emote.providerName,
|
||||||
this->callback_));
|
this->callback_));
|
||||||
|
|
||||||
if (count++ == maxEmoteCount)
|
if (count++ == maxEntryCount)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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);
|
return this->ui_.listView->eventFilter(watched, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmoteInputPopup::setInputAction(ActionCallback callback)
|
void InputCompletionPopup::setInputAction(ActionCallback callback)
|
||||||
{
|
{
|
||||||
this->callback_ = std::move(callback);
|
this->callback_ = std::move(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmoteInputPopup::showEvent(QShowEvent *)
|
void InputCompletionPopup::showEvent(QShowEvent *)
|
||||||
{
|
{
|
||||||
this->redrawTimer_.start();
|
this->redrawTimer_.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmoteInputPopup::hideEvent(QHideEvent *)
|
void InputCompletionPopup::hideEvent(QHideEvent *)
|
||||||
{
|
{
|
||||||
this->redrawTimer_.stop();
|
this->redrawTimer_.stop();
|
||||||
}
|
}
|
||||||
+4
-3
@@ -9,16 +9,17 @@ namespace chatterino {
|
|||||||
|
|
||||||
class GenericListView;
|
class GenericListView;
|
||||||
|
|
||||||
class EmoteInputPopup : public BasePopup
|
class InputCompletionPopup : public BasePopup
|
||||||
{
|
{
|
||||||
using ActionCallback = std::function<void(const QString &)>;
|
using ActionCallback = std::function<void(const QString &)>;
|
||||||
|
|
||||||
constexpr static int maxEmoteCount = 200;
|
constexpr static int maxEntryCount = 200;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
EmoteInputPopup(QWidget *parent = nullptr);
|
InputCompletionPopup(QWidget *parent = nullptr);
|
||||||
|
|
||||||
void updateEmotes(const QString &text, ChannelPtr channel);
|
void updateEmotes(const QString &text, ChannelPtr channel);
|
||||||
|
void updateUsers(const QString &text, ChannelPtr channel);
|
||||||
virtual bool eventFilter(QObject *, QEvent *event) override;
|
virtual bool eventFilter(QObject *, QEvent *event) override;
|
||||||
|
|
||||||
void setInputAction(ActionCallback callback);
|
void setInputAction(ActionCallback callback);
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
#include "widgets/helper/ChannelView.hpp"
|
#include "widgets/helper/ChannelView.hpp"
|
||||||
#include "widgets/helper/EffectLabel.hpp"
|
#include "widgets/helper/EffectLabel.hpp"
|
||||||
#include "widgets/helper/ResizingTextEdit.hpp"
|
#include "widgets/helper/ResizingTextEdit.hpp"
|
||||||
#include "widgets/splits/EmoteInputPopup.hpp"
|
#include "widgets/splits/InputCompletionPopup.hpp"
|
||||||
#include "widgets/splits/Split.hpp"
|
#include "widgets/splits/Split.hpp"
|
||||||
#include "widgets/splits/SplitContainer.hpp"
|
#include "widgets/splits/SplitContainer.hpp"
|
||||||
#include "widgets/splits/SplitInput.hpp"
|
#include "widgets/splits/SplitInput.hpp"
|
||||||
@@ -45,7 +45,7 @@ SplitInput::SplitInput(Split *_chatWidget)
|
|||||||
// misc
|
// misc
|
||||||
this->installKeyPressedEvent();
|
this->installKeyPressedEvent();
|
||||||
this->ui_.textEdit->focusLost.connect([this] {
|
this->ui_.textEdit->focusLost.connect([this] {
|
||||||
this->hideColonMenu();
|
this->hideCompletionPopup();
|
||||||
});
|
});
|
||||||
this->scaleChangedEvent(this->scale());
|
this->scaleChangedEvent(this->scale());
|
||||||
}
|
}
|
||||||
@@ -202,7 +202,7 @@ void SplitInput::installKeyPressedEvent()
|
|||||||
auto app = getApp();
|
auto app = getApp();
|
||||||
|
|
||||||
this->ui_.textEdit->keyPressed.connect([this, app](QKeyEvent *event) {
|
this->ui_.textEdit->keyPressed.connect([this, app](QKeyEvent *event) {
|
||||||
if (auto popup = this->emoteInputPopup_.get())
|
if (auto popup = this->inputCompletionPopup_.get())
|
||||||
{
|
{
|
||||||
if (popup->isVisible())
|
if (popup->isVisible())
|
||||||
{
|
{
|
||||||
@@ -451,26 +451,30 @@ void SplitInput::installKeyPressedEvent()
|
|||||||
|
|
||||||
void SplitInput::onTextChanged()
|
void SplitInput::onTextChanged()
|
||||||
{
|
{
|
||||||
this->updateColonMenu();
|
this->updateCompletionPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SplitInput::onCursorPositionChanged()
|
void SplitInput::onCursorPositionChanged()
|
||||||
{
|
{
|
||||||
this->updateColonMenu();
|
this->updateCompletionPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SplitInput::updateColonMenu()
|
void SplitInput::updateCompletionPopup()
|
||||||
{
|
{
|
||||||
auto channel = this->split_->getChannel().get();
|
auto channel = this->split_->getChannel().get();
|
||||||
if (!getSettings()->emoteCompletionWithColon ||
|
auto tc = dynamic_cast<TwitchChannel *>(channel);
|
||||||
(!dynamic_cast<TwitchChannel *>(channel) &&
|
bool showEmoteCompletion =
|
||||||
!(channel->getType() == Channel::Type::TwitchWhispers)))
|
getSettings()->emoteCompletionWithColon &&
|
||||||
|
(tc || (channel->getType() == Channel::Type::TwitchWhispers));
|
||||||
|
bool showUsernameCompletion =
|
||||||
|
tc && getSettings()->showUsernameCompletionMenu;
|
||||||
|
if (!showEmoteCompletion && !showUsernameCompletion)
|
||||||
{
|
{
|
||||||
this->hideColonMenu();
|
this->hideCompletionPopup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if in :
|
// check if in completion prefix
|
||||||
auto &edit = *this->ui_.textEdit;
|
auto &edit = *this->ui_.textEdit;
|
||||||
|
|
||||||
auto text = edit.toPlainText();
|
auto text = edit.toPlainText();
|
||||||
@@ -478,7 +482,7 @@ void SplitInput::updateColonMenu()
|
|||||||
|
|
||||||
if (text.length() == 0)
|
if (text.length() == 0)
|
||||||
{
|
{
|
||||||
this->hideColonMenu();
|
this->hideCompletionPopup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -486,41 +490,54 @@ void SplitInput::updateColonMenu()
|
|||||||
{
|
{
|
||||||
if (text[i] == ' ')
|
if (text[i] == ' ')
|
||||||
{
|
{
|
||||||
this->hideColonMenu();
|
this->hideCompletionPopup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (text[i] == ':')
|
else if (text[i] == ':' && showEmoteCompletion)
|
||||||
{
|
{
|
||||||
if (i == 0 || text[i - 1].isSpace())
|
if (i == 0 || text[i - 1].isSpace())
|
||||||
this->showColonMenu(text.mid(i, position - i + 1).mid(1));
|
this->showCompletionPopup(text.mid(i, position - i + 1).mid(1),
|
||||||
|
true);
|
||||||
else
|
else
|
||||||
this->hideColonMenu();
|
this->hideCompletionPopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (text[i] == '@' && showUsernameCompletion)
|
||||||
|
{
|
||||||
|
if (i == 0 || text[i - 1].isSpace())
|
||||||
|
this->showCompletionPopup(text.mid(i, position - i + 1).mid(1),
|
||||||
|
false);
|
||||||
|
else
|
||||||
|
this->hideCompletionPopup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->hideColonMenu();
|
this->hideCompletionPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SplitInput::showColonMenu(const QString &text)
|
void SplitInput::showCompletionPopup(const QString &text, bool emoteCompletion)
|
||||||
{
|
{
|
||||||
if (!this->emoteInputPopup_.get())
|
if (!this->inputCompletionPopup_.get())
|
||||||
{
|
{
|
||||||
this->emoteInputPopup_ = new EmoteInputPopup(this);
|
this->inputCompletionPopup_ = new InputCompletionPopup(this);
|
||||||
this->emoteInputPopup_->setInputAction(
|
this->inputCompletionPopup_->setInputAction(
|
||||||
[that = QObjectRef(this)](const QString &text) mutable {
|
[that = QObjectRef(this)](const QString &text) mutable {
|
||||||
if (auto this2 = that.get())
|
if (auto this2 = that.get())
|
||||||
{
|
{
|
||||||
this2->insertColonText(text);
|
this2->insertCompletionText(text);
|
||||||
this2->hideColonMenu();
|
this2->hideCompletionPopup();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
auto popup = this->emoteInputPopup_.get();
|
auto popup = this->inputCompletionPopup_.get();
|
||||||
assert(popup);
|
assert(popup);
|
||||||
|
|
||||||
popup->updateEmotes(text, this->split_->getChannel());
|
if (emoteCompletion) // autocomplete emotes
|
||||||
|
popup->updateEmotes(text, this->split_->getChannel());
|
||||||
|
else // autocomplete usernames
|
||||||
|
popup->updateUsers(text, this->split_->getChannel());
|
||||||
|
|
||||||
auto pos = this->mapToGlobal({0, 0}) - QPoint(0, popup->height()) +
|
auto pos = this->mapToGlobal({0, 0}) - QPoint(0, popup->height()) +
|
||||||
QPoint((this->width() - popup->width()) / 2, 0);
|
QPoint((this->width() - popup->width()) / 2, 0);
|
||||||
@@ -529,13 +546,13 @@ void SplitInput::showColonMenu(const QString &text)
|
|||||||
popup->show();
|
popup->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SplitInput::hideColonMenu()
|
void SplitInput::hideCompletionPopup()
|
||||||
{
|
{
|
||||||
if (auto popup = this->emoteInputPopup_.get())
|
if (auto popup = this->inputCompletionPopup_.get())
|
||||||
popup->hide();
|
popup->hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SplitInput::insertColonText(const QString &input_)
|
void SplitInput::insertCompletionText(const QString &input_)
|
||||||
{
|
{
|
||||||
auto &edit = *this->ui_.textEdit;
|
auto &edit = *this->ui_.textEdit;
|
||||||
auto input = input_ + ' ';
|
auto input = input_ + ' ';
|
||||||
@@ -545,10 +562,21 @@ void SplitInput::insertColonText(const QString &input_)
|
|||||||
|
|
||||||
for (int i = clamp(position, 0, text.length() - 1); i >= 0; i--)
|
for (int i = clamp(position, 0, text.length() - 1); i >= 0; i--)
|
||||||
{
|
{
|
||||||
|
bool done = false;
|
||||||
if (text[i] == ':')
|
if (text[i] == ':')
|
||||||
{
|
{
|
||||||
auto cursor = edit.textCursor();
|
done = true;
|
||||||
|
}
|
||||||
|
else if (text[i] == '@')
|
||||||
|
{
|
||||||
|
input = "@" + input_ +
|
||||||
|
(getSettings()->mentionUsersWithComma ? ", " : " ");
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (done)
|
||||||
|
{
|
||||||
|
auto cursor = edit.textCursor();
|
||||||
edit.setText(text.remove(i, position - i).insert(i, input));
|
edit.setText(text.remove(i, position - i).insert(i, input));
|
||||||
|
|
||||||
cursor.setPosition(i + input.size());
|
cursor.setPosition(i + input.size());
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace chatterino {
|
|||||||
|
|
||||||
class Split;
|
class Split;
|
||||||
class EmotePopup;
|
class EmotePopup;
|
||||||
class EmoteInputPopup;
|
class InputCompletionPopup;
|
||||||
class EffectLabel;
|
class EffectLabel;
|
||||||
class ResizingTextEdit;
|
class ResizingTextEdit;
|
||||||
|
|
||||||
@@ -48,15 +48,15 @@ private:
|
|||||||
void onCursorPositionChanged();
|
void onCursorPositionChanged();
|
||||||
void onTextChanged();
|
void onTextChanged();
|
||||||
void updateEmoteButton();
|
void updateEmoteButton();
|
||||||
void updateColonMenu();
|
void updateCompletionPopup();
|
||||||
void showColonMenu(const QString &text);
|
void showCompletionPopup(const QString &text, bool emoteCompletion);
|
||||||
void hideColonMenu();
|
void hideCompletionPopup();
|
||||||
void insertColonText(const QString &text);
|
void insertCompletionText(const QString &text);
|
||||||
void openEmotePopup();
|
void openEmotePopup();
|
||||||
|
|
||||||
Split *const split_;
|
Split *const split_;
|
||||||
QObjectRef<EmotePopup> emotePopup_;
|
QObjectRef<EmotePopup> emotePopup_;
|
||||||
QObjectRef<EmoteInputPopup> emoteInputPopup_;
|
QObjectRef<InputCompletionPopup> inputCompletionPopup_;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
ResizingTextEdit *textEdit;
|
ResizingTextEdit *textEdit;
|
||||||
|
|||||||
Reference in New Issue
Block a user