mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
Show context menu anywhere in MessageLayout when applicable (#3566)
Co-authored-by: James Upjohn <jammehcow@jammehcow.co.nz> Co-authored-by: Rasmus Karlsson <rasmus.karlsson@pajlada.com>
This commit is contained in:
parent
fb9c3ad42b
commit
5978ed8b1f
|
@ -53,6 +53,7 @@
|
|||
- Minor: Removed timestamp from AutoMod messages. (#3503)
|
||||
- Minor: Added ability to copy message ID with `Shift + Right Click`. (#3481)
|
||||
- Minor: Colorize the entire split header when focused. (#3379)
|
||||
- Minor: Show right click context menu anywhere within a message's line. (#3566)
|
||||
- Minor: Make Tab Layout setting only accept predefined values (#3564)
|
||||
- Bugfix: Fix Split Input hotkeys not being available when input is hidden (#3362)
|
||||
- Bugfix: Fixed colored usernames sometimes not working. (#3170)
|
||||
|
|
|
@ -1762,11 +1762,6 @@ void ChannelView::mouseReleaseEvent(QMouseEvent *event)
|
|||
}
|
||||
}
|
||||
|
||||
if (hoverLayoutElement == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// handle the click
|
||||
this->handleMouseClick(event, hoverLayoutElement, layout);
|
||||
|
||||
|
@ -1790,7 +1785,12 @@ void ChannelView::handleMouseClick(QMouseEvent *event,
|
|||
this->queueLayout();
|
||||
}
|
||||
|
||||
auto &link = hoveredElement->getLink();
|
||||
if (hoveredElement == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &link = hoveredElement->getLink();
|
||||
if (!getSettings()->linksDoubleClickOnly)
|
||||
{
|
||||
this->handleLinkClick(event, link, layout.get());
|
||||
|
@ -1812,28 +1812,39 @@ void ChannelView::handleMouseClick(QMouseEvent *event,
|
|||
}
|
||||
};
|
||||
|
||||
auto &link = hoveredElement->getLink();
|
||||
if (hoveredElement != nullptr)
|
||||
{
|
||||
const auto &link = hoveredElement->getLink();
|
||||
|
||||
if (link.type == Link::UserInfo)
|
||||
{
|
||||
const bool commaMention = getSettings()->mentionUsersWithComma;
|
||||
const bool commaMention =
|
||||
getSettings()->mentionUsersWithComma;
|
||||
const bool isFirstWord =
|
||||
split && split->getInput().isEditFirstWord();
|
||||
auto userMention =
|
||||
formatUserMention(link.value, isFirstWord, commaMention);
|
||||
auto userMention = formatUserMention(
|
||||
link.value, isFirstWord, commaMention);
|
||||
insertText("@" + userMention + " ");
|
||||
return;
|
||||
}
|
||||
else if (link.type == Link::UserWhisper)
|
||||
|
||||
if (link.type == Link::UserWhisper)
|
||||
{
|
||||
insertText("/w " + link.value + " ");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
this->addContextMenuItems(hoveredElement, layout, event);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Qt::MiddleButton: {
|
||||
auto &link = hoveredElement->getLink();
|
||||
if (hoveredElement == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &link = hoveredElement->getLink();
|
||||
if (!getSettings()->linksDoubleClickOnly)
|
||||
{
|
||||
this->handleLinkClick(event, link, layout.get());
|
||||
|
@ -1848,9 +1859,6 @@ void ChannelView::addContextMenuItems(
|
|||
const MessageLayoutElement *hoveredElement, MessageLayoutPtr layout,
|
||||
QMouseEvent *event)
|
||||
{
|
||||
const auto &creator = hoveredElement->getCreator();
|
||||
auto creatorFlags = creator.getFlags();
|
||||
|
||||
static QMenu *previousMenu = nullptr;
|
||||
if (previousMenu != nullptr)
|
||||
{
|
||||
|
@ -1861,12 +1869,45 @@ void ChannelView::addContextMenuItems(
|
|||
auto menu = new QMenu;
|
||||
previousMenu = menu;
|
||||
|
||||
// Add image options if the element clicked contains an image (e.g. a badge or an emote)
|
||||
this->addImageContextMenuItems(hoveredElement, layout, event, *menu);
|
||||
|
||||
// Add link options if the element clicked contains a link
|
||||
this->addLinkContextMenuItems(hoveredElement, layout, event, *menu);
|
||||
|
||||
// Add message options
|
||||
this->addMessageContextMenuItems(hoveredElement, layout, event, *menu);
|
||||
|
||||
// Add Twitch-specific link options if the element clicked contains a link detected as a Twitch username
|
||||
this->addTwitchLinkContextMenuItems(hoveredElement, layout, event, *menu);
|
||||
|
||||
// Add hidden options (e.g. copy message ID) if the user held down Shift
|
||||
this->addHiddenContextMenuItems(hoveredElement, layout, event, *menu);
|
||||
|
||||
menu->popup(QCursor::pos());
|
||||
menu->raise();
|
||||
}
|
||||
|
||||
void ChannelView::addImageContextMenuItems(
|
||||
const MessageLayoutElement *hoveredElement, MessageLayoutPtr /*layout*/,
|
||||
QMouseEvent * /*event*/, QMenu &menu)
|
||||
{
|
||||
if (hoveredElement == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &creator = hoveredElement->getCreator();
|
||||
auto creatorFlags = creator.getFlags();
|
||||
|
||||
// Badge actions
|
||||
if (creatorFlags.hasAny({MessageElementFlag::Badges}))
|
||||
{
|
||||
if (auto badgeElement = dynamic_cast<const BadgeElement *>(&creator))
|
||||
{
|
||||
addEmoteContextMenuItems(*badgeElement->getEmote(), creatorFlags,
|
||||
*menu);
|
||||
menu);
|
||||
}
|
||||
}
|
||||
|
||||
// Emote actions
|
||||
|
@ -1874,48 +1915,68 @@ void ChannelView::addContextMenuItems(
|
|||
{MessageElementFlag::EmoteImages, MessageElementFlag::EmojiImage}))
|
||||
{
|
||||
if (auto emoteElement = dynamic_cast<const EmoteElement *>(&creator))
|
||||
{
|
||||
addEmoteContextMenuItems(*emoteElement->getEmote(), creatorFlags,
|
||||
*menu);
|
||||
menu);
|
||||
}
|
||||
}
|
||||
|
||||
// add seperator
|
||||
if (!menu->actions().empty())
|
||||
if (!menu.actions().empty())
|
||||
{
|
||||
menu->addSeparator();
|
||||
menu.addSeparator();
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelView::addLinkContextMenuItems(
|
||||
const MessageLayoutElement *hoveredElement, MessageLayoutPtr /*layout*/,
|
||||
QMouseEvent * /*event*/, QMenu &menu)
|
||||
{
|
||||
if (hoveredElement == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &link = hoveredElement->getLink();
|
||||
|
||||
if (link.type != Link::Url)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Link copy
|
||||
if (hoveredElement->getLink().type == Link::Url)
|
||||
{
|
||||
QString url = hoveredElement->getLink().value;
|
||||
QString url = link.value;
|
||||
|
||||
// open link
|
||||
menu->addAction("Open link", [url] {
|
||||
menu.addAction("Open link", [url] {
|
||||
QDesktopServices::openUrl(QUrl(url));
|
||||
});
|
||||
// open link default
|
||||
if (supportsIncognitoLinks())
|
||||
{
|
||||
menu->addAction("Open link incognito", [url] {
|
||||
menu.addAction("Open link incognito", [url] {
|
||||
openLinkIncognito(url);
|
||||
});
|
||||
}
|
||||
menu->addAction("Copy link", [url] {
|
||||
menu.addAction("Copy link", [url] {
|
||||
crossPlatformCopy(url);
|
||||
});
|
||||
|
||||
menu->addSeparator();
|
||||
menu.addSeparator();
|
||||
}
|
||||
|
||||
void ChannelView::addMessageContextMenuItems(
|
||||
const MessageLayoutElement * /*hoveredElement*/, MessageLayoutPtr layout,
|
||||
QMouseEvent * /*event*/, QMenu &menu)
|
||||
{
|
||||
// Copy actions
|
||||
if (!this->selection_.isEmpty())
|
||||
{
|
||||
menu->addAction("Copy selection", [this] {
|
||||
menu.addAction("Copy selection", [this] {
|
||||
crossPlatformCopy(this->getSelectedText());
|
||||
});
|
||||
}
|
||||
|
||||
menu->addAction("Copy message", [layout] {
|
||||
menu.addAction("Copy message", [layout] {
|
||||
QString copyString;
|
||||
layout->addSelectionText(copyString, 0, INT_MAX,
|
||||
CopyMode::OnlyTextAndEmotes);
|
||||
|
@ -1923,16 +1984,30 @@ void ChannelView::addContextMenuItems(
|
|||
crossPlatformCopy(copyString);
|
||||
});
|
||||
|
||||
menu->addAction("Copy full message", [layout] {
|
||||
menu.addAction("Copy full message", [layout] {
|
||||
QString copyString;
|
||||
layout->addSelectionText(copyString);
|
||||
|
||||
crossPlatformCopy(copyString);
|
||||
});
|
||||
}
|
||||
|
||||
// If is a link to a Twitch user/stream
|
||||
if (hoveredElement->getLink().type == Link::Url)
|
||||
void ChannelView::addTwitchLinkContextMenuItems(
|
||||
const MessageLayoutElement *hoveredElement, MessageLayoutPtr /*layout*/,
|
||||
QMouseEvent * /*event*/, QMenu &menu)
|
||||
{
|
||||
if (hoveredElement == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &link = hoveredElement->getLink();
|
||||
|
||||
if (link.type != Link::Url)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static QRegularExpression twitchChannelRegex(
|
||||
R"(^(?:https?:\/\/)?(?:www\.|go\.)?twitch\.tv\/(?:popout\/)?(?<username>[a-z0-9_]{3,}))",
|
||||
QRegularExpression::CaseInsensitiveOption);
|
||||
|
@ -1956,49 +2031,55 @@ void ChannelView::addContextMenuItems(
|
|||
"wallet", //
|
||||
};
|
||||
|
||||
auto twitchMatch =
|
||||
twitchChannelRegex.match(hoveredElement->getLink().value);
|
||||
auto twitchMatch = twitchChannelRegex.match(link.value);
|
||||
auto twitchUsername = twitchMatch.captured("username");
|
||||
if (!twitchUsername.isEmpty() &&
|
||||
!ignoredUsernames.contains(twitchUsername))
|
||||
if (!twitchUsername.isEmpty() && !ignoredUsernames.contains(twitchUsername))
|
||||
{
|
||||
menu->addSeparator();
|
||||
menu->addAction("Open in new split", [twitchUsername, this] {
|
||||
menu.addSeparator();
|
||||
menu.addAction("Open in new split", [twitchUsername, this] {
|
||||
this->openChannelIn.invoke(twitchUsername,
|
||||
FromTwitchLinkOpenChannelIn::Split);
|
||||
});
|
||||
menu->addAction("Open in new tab", [twitchUsername, this] {
|
||||
menu.addAction("Open in new tab", [twitchUsername, this] {
|
||||
this->openChannelIn.invoke(twitchUsername,
|
||||
FromTwitchLinkOpenChannelIn::Tab);
|
||||
});
|
||||
|
||||
menu->addSeparator();
|
||||
menu->addAction("Open player in browser", [twitchUsername, this] {
|
||||
menu.addSeparator();
|
||||
menu.addAction("Open player in browser", [twitchUsername, this] {
|
||||
this->openChannelIn.invoke(
|
||||
twitchUsername, FromTwitchLinkOpenChannelIn::BrowserPlayer);
|
||||
});
|
||||
menu->addAction("Open in streamlink", [twitchUsername, this] {
|
||||
this->openChannelIn.invoke(
|
||||
twitchUsername, FromTwitchLinkOpenChannelIn::Streamlink);
|
||||
menu.addAction("Open in streamlink", [twitchUsername, this] {
|
||||
this->openChannelIn.invoke(twitchUsername,
|
||||
FromTwitchLinkOpenChannelIn::Streamlink);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (event->modifiers() == Qt::ShiftModifier &&
|
||||
!layout->getMessage()->id.isEmpty())
|
||||
void ChannelView::addHiddenContextMenuItems(
|
||||
const MessageLayoutElement * /*hoveredElement*/, MessageLayoutPtr layout,
|
||||
QMouseEvent *event, QMenu &menu)
|
||||
{
|
||||
menu->addAction("Copy message ID",
|
||||
if (!layout)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->modifiers() != Qt::ShiftModifier)
|
||||
{
|
||||
// NOTE: We currently require the modifier to be ONLY shift - we might want to check if shift is among the modifiers instead
|
||||
return;
|
||||
}
|
||||
|
||||
if (!layout->getMessage()->id.isEmpty())
|
||||
{
|
||||
menu.addAction("Copy message ID",
|
||||
[messageID = layout->getMessage()->id] {
|
||||
crossPlatformCopy(messageID);
|
||||
});
|
||||
}
|
||||
|
||||
menu->popup(QCursor::pos());
|
||||
menu->raise();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void ChannelView::mouseDoubleClickEvent(QMouseEvent *event)
|
||||
{
|
||||
std::shared_ptr<MessageLayout> layout;
|
||||
|
|
|
@ -157,10 +157,25 @@ private:
|
|||
const QPoint &relativePos, int &wordStart, int &wordEnd);
|
||||
|
||||
void handleMouseClick(QMouseEvent *event,
|
||||
const MessageLayoutElement *hoverLayoutElement,
|
||||
const MessageLayoutElement *hoveredElement,
|
||||
MessageLayoutPtr layout);
|
||||
void addContextMenuItems(const MessageLayoutElement *hoveredElement,
|
||||
MessageLayoutPtr layout, QMouseEvent *event);
|
||||
void addImageContextMenuItems(const MessageLayoutElement *hoveredElement,
|
||||
MessageLayoutPtr layout, QMouseEvent *event,
|
||||
QMenu &menu);
|
||||
void addLinkContextMenuItems(const MessageLayoutElement *hoveredElement,
|
||||
MessageLayoutPtr layout, QMouseEvent *event,
|
||||
QMenu &menu);
|
||||
void addMessageContextMenuItems(const MessageLayoutElement *hoveredElement,
|
||||
MessageLayoutPtr layout, QMouseEvent *event,
|
||||
QMenu &menu);
|
||||
void addTwitchLinkContextMenuItems(
|
||||
const MessageLayoutElement *hoveredElement, MessageLayoutPtr layout,
|
||||
QMouseEvent *event, QMenu &menu);
|
||||
void addHiddenContextMenuItems(const MessageLayoutElement *hoveredElement,
|
||||
MessageLayoutPtr layout, QMouseEvent *event,
|
||||
QMenu &menu);
|
||||
int getLayoutWidth() const;
|
||||
void updatePauses();
|
||||
void unpaused();
|
||||
|
|
Loading…
Reference in a new issue