mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
feat: add Go to message
action in various places (#3953)
* feat: add `Go to message` action in search popup * chore: add changelog entry * fix: only scroll if the scrollbar is shown * fix: go to message when view isn't focused * feat: animate highlighted message * fix: missing includes * fix: order of initialization * fix: add `ChannelView::mayContainMessage` to filter messages * feat: add `Go to message` action in `/mentions` * fix: ignore any mentions channel when searching for split * feat: add `Go to message` action in reply-threads * fix: remove redundant `source` parameter * feat: add `Go to message` action in user-cards * feat: add link to deleted message * fix: set current time to 0 when starting animation * chore: update changelog * fix: add default case (unreachable) * chore: removed unused variable * fix: search in mentions * fix: always attempt to focus split * fix: rename `Link::MessageId` to `Link::JumpToMessage` * fix: rename `selectAndScrollToMessage` to `scrollToMessage` * fix: rename internal `scrollToMessage` to `scrollToMessageLayout` * fix: deleted message link in search popup * chore: reword explanation * fix: use for-loop instead of `std::find_if` * refactor: define highlight colors in `BaseTheme` * core: replace `iff` with `if` * fix: only return if the message found * Reword/phrase/dot changelog entries Co-authored-by: pajlada <rasmus.karlsson@pajlada.com>
This commit is contained in:
parent
5655a7d718
commit
be72d73c3d
14 changed files with 344 additions and 12 deletions
|
@ -32,6 +32,8 @@
|
||||||
- Minor: Add settings to toggle BTTV/FFZ global/channel emotes (#3935)
|
- Minor: Add settings to toggle BTTV/FFZ global/channel emotes (#3935)
|
||||||
- Minor: Add AutoMod message flag filter. (#3938)
|
- Minor: Add AutoMod message flag filter. (#3938)
|
||||||
- Minor: Added whitespace trim to username field in nicknames (#3946)
|
- Minor: Added whitespace trim to username field in nicknames (#3946)
|
||||||
|
- Minor: Added `Go to message` context menu action to search popup, mentions, usercard and reply threads. (#3953)
|
||||||
|
- Minor: Added link back to original message that was deleted. (#3953)
|
||||||
- Bugfix: Fix crash that can occur when closing and quickly reopening a split, then running a command. (#3852)
|
- Bugfix: Fix crash that can occur when closing and quickly reopening a split, then running a command. (#3852)
|
||||||
- Bugfix: Connection to Twitch PubSub now recovers more reliably. (#3643, #3716)
|
- Bugfix: Connection to Twitch PubSub now recovers more reliably. (#3643, #3716)
|
||||||
- Bugfix: Fix crash that can occur when changing channels. (#3799)
|
- Bugfix: Fix crash that can occur when changing channels. (#3799)
|
||||||
|
|
|
@ -168,6 +168,12 @@ void AB_THEME_CLASS::actuallyUpdate(double hue, double multiplier)
|
||||||
// this->messages.seperator =
|
// this->messages.seperator =
|
||||||
// this->messages.seperatorInner =
|
// this->messages.seperatorInner =
|
||||||
|
|
||||||
|
int complementaryGray = this->isLightTheme() ? 20 : 230;
|
||||||
|
this->messages.highlightAnimationStart =
|
||||||
|
QColor(complementaryGray, complementaryGray, complementaryGray, 110);
|
||||||
|
this->messages.highlightAnimationEnd =
|
||||||
|
QColor(complementaryGray, complementaryGray, complementaryGray, 0);
|
||||||
|
|
||||||
// Scrollbar
|
// Scrollbar
|
||||||
this->scrollbars.background = QColor(0, 0, 0, 0);
|
this->scrollbars.background = QColor(0, 0, 0, 0);
|
||||||
// this->scrollbars.background = splits.background;
|
// this->scrollbars.background = splits.background;
|
||||||
|
|
|
@ -74,6 +74,9 @@ public:
|
||||||
// QColor seperator;
|
// QColor seperator;
|
||||||
// QColor seperatorInner;
|
// QColor seperatorInner;
|
||||||
QColor selection;
|
QColor selection;
|
||||||
|
|
||||||
|
QColor highlightAnimationStart;
|
||||||
|
QColor highlightAnimationEnd;
|
||||||
} messages;
|
} messages;
|
||||||
|
|
||||||
/// SCROLLBAR
|
/// SCROLLBAR
|
||||||
|
|
|
@ -25,6 +25,7 @@ public:
|
||||||
CopyToClipboard,
|
CopyToClipboard,
|
||||||
ReplyToMessage,
|
ReplyToMessage,
|
||||||
ViewThread,
|
ViewThread,
|
||||||
|
JumpToMessage,
|
||||||
};
|
};
|
||||||
|
|
||||||
Link();
|
Link();
|
||||||
|
|
|
@ -66,6 +66,11 @@ int MessageLayout::getHeight() const
|
||||||
return container_->getHeight();
|
return container_->getHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int MessageLayout::getWidth() const
|
||||||
|
{
|
||||||
|
return this->container_->getWidth();
|
||||||
|
}
|
||||||
|
|
||||||
// Layout
|
// Layout
|
||||||
// return true if redraw is required
|
// return true if redraw is required
|
||||||
bool MessageLayout::layout(int width, float scale, MessageElementFlags flags)
|
bool MessageLayout::layout(int width, float scale, MessageElementFlags flags)
|
||||||
|
|
|
@ -40,6 +40,7 @@ public:
|
||||||
const MessagePtr &getMessagePtr() const;
|
const MessagePtr &getMessagePtr() const;
|
||||||
|
|
||||||
int getHeight() const;
|
int getHeight() const;
|
||||||
|
int getWidth() const;
|
||||||
|
|
||||||
MessageLayoutFlags flags;
|
MessageLayoutFlags flags;
|
||||||
|
|
||||||
|
|
|
@ -1471,15 +1471,17 @@ void TwitchMessageBuilder::deletionMessage(const MessagePtr originalMessage,
|
||||||
MessageColor::System);
|
MessageColor::System);
|
||||||
if (originalMessage->messageText.length() > 50)
|
if (originalMessage->messageText.length() > 50)
|
||||||
{
|
{
|
||||||
builder->emplace<TextElement>(
|
builder
|
||||||
originalMessage->messageText.left(50) + "…",
|
->emplace<TextElement>(originalMessage->messageText.left(50) + "…",
|
||||||
MessageElementFlag::Text, MessageColor::Text);
|
MessageElementFlag::Text, MessageColor::Text)
|
||||||
|
->setLink({Link::JumpToMessage, originalMessage->id});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
builder->emplace<TextElement>(originalMessage->messageText,
|
builder
|
||||||
MessageElementFlag::Text,
|
->emplace<TextElement>(originalMessage->messageText,
|
||||||
MessageColor::Text);
|
MessageElementFlag::Text, MessageColor::Text)
|
||||||
|
->setLink({Link::JumpToMessage, originalMessage->id});
|
||||||
}
|
}
|
||||||
builder->message().timeoutUser = "msg:" + originalMessage->id;
|
builder->message().timeoutUser = "msg:" + originalMessage->id;
|
||||||
}
|
}
|
||||||
|
@ -1511,14 +1513,17 @@ void TwitchMessageBuilder::deletionMessage(const DeleteAction &action,
|
||||||
MessageColor::System);
|
MessageColor::System);
|
||||||
if (action.messageText.length() > 50)
|
if (action.messageText.length() > 50)
|
||||||
{
|
{
|
||||||
builder->emplace<TextElement>(action.messageText.left(50) + "…",
|
builder
|
||||||
MessageElementFlag::Text,
|
->emplace<TextElement>(action.messageText.left(50) + "…",
|
||||||
MessageColor::Text);
|
MessageElementFlag::Text, MessageColor::Text)
|
||||||
|
->setLink({Link::JumpToMessage, action.messageId});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
builder->emplace<TextElement>(
|
builder
|
||||||
action.messageText, MessageElementFlag::Text, MessageColor::Text);
|
->emplace<TextElement>(action.messageText, MessageElementFlag::Text,
|
||||||
|
MessageColor::Text)
|
||||||
|
->setLink({Link::JumpToMessage, action.messageId});
|
||||||
}
|
}
|
||||||
builder->message().timeoutUser = "msg:" + action.messageId;
|
builder->message().timeoutUser = "msg:" + action.messageId;
|
||||||
}
|
}
|
||||||
|
|
|
@ -320,6 +320,11 @@ void WindowManager::select(SplitContainer *container)
|
||||||
this->selectSplitContainer.invoke(container);
|
this->selectSplitContainer.invoke(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WindowManager::scrollToMessage(const MessagePtr &message)
|
||||||
|
{
|
||||||
|
this->scrollToMessageSignal.invoke(message);
|
||||||
|
}
|
||||||
|
|
||||||
QPoint WindowManager::emotePopupPos()
|
QPoint WindowManager::emotePopupPos()
|
||||||
{
|
{
|
||||||
return this->emotePopupPos_;
|
return this->emotePopupPos_;
|
||||||
|
|
|
@ -15,6 +15,7 @@ class Settings;
|
||||||
class Paths;
|
class Paths;
|
||||||
class Window;
|
class Window;
|
||||||
class SplitContainer;
|
class SplitContainer;
|
||||||
|
class ChannelView;
|
||||||
|
|
||||||
enum class MessageElementFlag : int64_t;
|
enum class MessageElementFlag : int64_t;
|
||||||
using MessageElementFlags = FlagsEnum<MessageElementFlag>;
|
using MessageElementFlags = FlagsEnum<MessageElementFlag>;
|
||||||
|
@ -66,6 +67,13 @@ public:
|
||||||
|
|
||||||
void select(Split *split);
|
void select(Split *split);
|
||||||
void select(SplitContainer *container);
|
void select(SplitContainer *container);
|
||||||
|
/**
|
||||||
|
* Scrolls to the message in a split that's not
|
||||||
|
* a mentions view and focuses the split.
|
||||||
|
*
|
||||||
|
* @param message Message to scroll to.
|
||||||
|
*/
|
||||||
|
void scrollToMessage(const MessagePtr &message);
|
||||||
|
|
||||||
QPoint emotePopupPos();
|
QPoint emotePopupPos();
|
||||||
void setEmotePopupPos(QPoint pos);
|
void setEmotePopupPos(QPoint pos);
|
||||||
|
@ -105,6 +113,7 @@ public:
|
||||||
|
|
||||||
pajlada::Signals::Signal<Split *> selectSplit;
|
pajlada::Signals::Signal<Split *> selectSplit;
|
||||||
pajlada::Signals::Signal<SplitContainer *> selectSplitContainer;
|
pajlada::Signals::Signal<SplitContainer *> selectSplitContainer;
|
||||||
|
pajlada::Signals::Signal<const MessagePtr &> scrollToMessageSignal;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void encodeNodeRecursively(SplitContainer::Node *node,
|
static void encodeNodeRecursively(SplitContainer::Node *node,
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "util/InitUpdateButton.hpp"
|
#include "util/InitUpdateButton.hpp"
|
||||||
#include "widgets/Window.hpp"
|
#include "widgets/Window.hpp"
|
||||||
#include "widgets/dialogs/SettingsDialog.hpp"
|
#include "widgets/dialogs/SettingsDialog.hpp"
|
||||||
|
#include "widgets/helper/ChannelView.hpp"
|
||||||
#include "widgets/helper/NotebookButton.hpp"
|
#include "widgets/helper/NotebookButton.hpp"
|
||||||
#include "widgets/helper/NotebookTab.hpp"
|
#include "widgets/helper/NotebookTab.hpp"
|
||||||
#include "widgets/splits/Split.hpp"
|
#include "widgets/splits/Split.hpp"
|
||||||
|
@ -1006,6 +1007,29 @@ SplitNotebook::SplitNotebook(Window *parent)
|
||||||
[this](SplitContainer *sc) {
|
[this](SplitContainer *sc) {
|
||||||
this->select(sc);
|
this->select(sc);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this->signalHolder_.managedConnect(
|
||||||
|
getApp()->windows->scrollToMessageSignal,
|
||||||
|
[this](const MessagePtr &message) {
|
||||||
|
for (auto &&item : this->items())
|
||||||
|
{
|
||||||
|
if (auto sc = dynamic_cast<SplitContainer *>(item.page))
|
||||||
|
{
|
||||||
|
for (auto *split : sc->getSplits())
|
||||||
|
{
|
||||||
|
if (split->getChannel()->getType() !=
|
||||||
|
Channel::Type::TwitchMentions)
|
||||||
|
{
|
||||||
|
if (split->getChannelView().scrollToMessage(
|
||||||
|
message))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void SplitNotebook::showEvent(QShowEvent *)
|
void SplitNotebook::showEvent(QShowEvent *)
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
#include "ChannelView.hpp"
|
#include "ChannelView.hpp"
|
||||||
|
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
|
#include <QColor>
|
||||||
#include <QDate>
|
#include <QDate>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
|
#include <QEasingCurve>
|
||||||
#include <QGraphicsBlurEffect>
|
#include <QGraphicsBlurEffect>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QScreen>
|
#include <QScreen>
|
||||||
|
#include <QVariantAnimation>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
@ -118,12 +121,23 @@ namespace {
|
||||||
addPageLink("FFZ");
|
addPageLink("FFZ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Current function: https://www.desmos.com/calculator/vdyamchjwh
|
||||||
|
qreal highlightEasingFunction(qreal progress)
|
||||||
|
{
|
||||||
|
if (progress <= 0.1)
|
||||||
|
{
|
||||||
|
return 1.0 - pow(10.0 * progress, 3.0);
|
||||||
|
}
|
||||||
|
return 1.0 + pow((20.0 / 9.0) * (0.5 * progress - 0.5), 3.0);
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
ChannelView::ChannelView(BaseWidget *parent, Split *split, Context context)
|
ChannelView::ChannelView(BaseWidget *parent, Split *split, Context context)
|
||||||
: BaseWidget(parent)
|
: BaseWidget(parent)
|
||||||
, split_(split)
|
, split_(split)
|
||||||
, scrollBar_(new Scrollbar(this))
|
, scrollBar_(new Scrollbar(this))
|
||||||
|
, highlightAnimation_(this)
|
||||||
, context_(context)
|
, context_(context)
|
||||||
{
|
{
|
||||||
this->setMouseTracking(true);
|
this->setMouseTracking(true);
|
||||||
|
@ -164,6 +178,12 @@ ChannelView::ChannelView(BaseWidget *parent, Split *split, Context context)
|
||||||
// of any place where you can, or where it would make sense,
|
// of any place where you can, or where it would make sense,
|
||||||
// to tab to a ChannelVieChannelView
|
// to tab to a ChannelVieChannelView
|
||||||
this->setFocusPolicy(Qt::FocusPolicy::ClickFocus);
|
this->setFocusPolicy(Qt::FocusPolicy::ClickFocus);
|
||||||
|
|
||||||
|
this->setupHighlightAnimationColors();
|
||||||
|
this->highlightAnimation_.setDuration(1500);
|
||||||
|
auto curve = QEasingCurve();
|
||||||
|
curve.setCustomType(highlightEasingFunction);
|
||||||
|
this->highlightAnimation_.setEasingCurve(curve);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChannelView::initializeLayout()
|
void ChannelView::initializeLayout()
|
||||||
|
@ -339,9 +359,18 @@ void ChannelView::themeChangedEvent()
|
||||||
{
|
{
|
||||||
BaseWidget::themeChangedEvent();
|
BaseWidget::themeChangedEvent();
|
||||||
|
|
||||||
|
this->setupHighlightAnimationColors();
|
||||||
this->queueLayout();
|
this->queueLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChannelView::setupHighlightAnimationColors()
|
||||||
|
{
|
||||||
|
this->highlightAnimation_.setStartValue(
|
||||||
|
this->theme->messages.highlightAnimationStart);
|
||||||
|
this->highlightAnimation_.setEndValue(
|
||||||
|
this->theme->messages.highlightAnimationEnd);
|
||||||
|
}
|
||||||
|
|
||||||
void ChannelView::scaleChangedEvent(float scale)
|
void ChannelView::scaleChangedEvent(float scale)
|
||||||
{
|
{
|
||||||
BaseWidget::scaleChangedEvent(scale);
|
BaseWidget::scaleChangedEvent(scale);
|
||||||
|
@ -392,7 +421,8 @@ void ChannelView::performLayout(bool causedByScrollbar)
|
||||||
auto &messages = this->getMessagesSnapshot();
|
auto &messages = this->getMessagesSnapshot();
|
||||||
|
|
||||||
this->showingLatestMessages_ =
|
this->showingLatestMessages_ =
|
||||||
this->scrollBar_->isAtBottom() || !this->scrollBar_->isVisible();
|
this->scrollBar_->isAtBottom() ||
|
||||||
|
(!this->scrollBar_->isVisible() && !causedByScrollbar);
|
||||||
|
|
||||||
/// Layout visible messages
|
/// Layout visible messages
|
||||||
this->layoutVisibleMessages(messages);
|
this->layoutVisibleMessages(messages);
|
||||||
|
@ -475,6 +505,7 @@ void ChannelView::updateScrollbar(
|
||||||
{
|
{
|
||||||
this->scrollBar_->setDesiredValue(0);
|
this->scrollBar_->setDesiredValue(0);
|
||||||
}
|
}
|
||||||
|
this->showScrollBar_ = showScrollbar;
|
||||||
|
|
||||||
this->scrollBar_->setMaximum(messages.size());
|
this->scrollBar_->setMaximum(messages.size());
|
||||||
|
|
||||||
|
@ -1088,6 +1119,86 @@ MessageElementFlags ChannelView::getFlags() const
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ChannelView::scrollToMessage(const MessagePtr &message)
|
||||||
|
{
|
||||||
|
if (!this->mayContainMessage(message))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &messagesSnapshot = this->getMessagesSnapshot();
|
||||||
|
if (messagesSnapshot.size() == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Figure out if we can somehow binary-search here.
|
||||||
|
// Currently, a message only sometimes stores a QDateTime,
|
||||||
|
// but always a QTime (inaccurate on midnight).
|
||||||
|
//
|
||||||
|
// We're searching from the bottom since it's more likely for a user
|
||||||
|
// wanting to go to a message that recently scrolled out of view.
|
||||||
|
size_t messageIdx = messagesSnapshot.size() - 1;
|
||||||
|
for (; messageIdx < SIZE_MAX; messageIdx--)
|
||||||
|
{
|
||||||
|
if (messagesSnapshot[messageIdx]->getMessagePtr() == message)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messageIdx == SIZE_MAX)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->scrollToMessageLayout(messagesSnapshot[messageIdx].get(), messageIdx);
|
||||||
|
getApp()->windows->select(this->split_);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChannelView::scrollToMessageId(const QString &messageId)
|
||||||
|
{
|
||||||
|
auto &messagesSnapshot = this->getMessagesSnapshot();
|
||||||
|
if (messagesSnapshot.size() == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're searching from the bottom since it's more likely for a user
|
||||||
|
// wanting to go to a message that recently scrolled out of view.
|
||||||
|
size_t messageIdx = messagesSnapshot.size() - 1;
|
||||||
|
for (; messageIdx < SIZE_MAX; messageIdx--)
|
||||||
|
{
|
||||||
|
if (messagesSnapshot[messageIdx]->getMessagePtr()->id == messageId)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messageIdx == SIZE_MAX)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->scrollToMessageLayout(messagesSnapshot[messageIdx].get(), messageIdx);
|
||||||
|
getApp()->windows->select(this->split_);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChannelView::scrollToMessageLayout(MessageLayout *layout,
|
||||||
|
size_t messageIdx)
|
||||||
|
{
|
||||||
|
this->highlightedMessage_ = layout;
|
||||||
|
this->highlightAnimation_.setCurrentTime(0);
|
||||||
|
this->highlightAnimation_.start(QAbstractAnimation::KeepWhenStopped);
|
||||||
|
|
||||||
|
if (this->showScrollBar_)
|
||||||
|
{
|
||||||
|
this->getScrollBar().setDesiredValue(messageIdx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ChannelView::paintEvent(QPaintEvent * /*event*/)
|
void ChannelView::paintEvent(QPaintEvent * /*event*/)
|
||||||
{
|
{
|
||||||
// BenchmarkGuard benchmark("paint");
|
// BenchmarkGuard benchmark("paint");
|
||||||
|
@ -1144,6 +1255,17 @@ void ChannelView::drawMessages(QPainter &painter)
|
||||||
layout->paint(painter, DRAW_WIDTH, y, i, this->selection_,
|
layout->paint(painter, DRAW_WIDTH, y, i, this->selection_,
|
||||||
isLastMessage, windowFocused, isMentions);
|
isLastMessage, windowFocused, isMentions);
|
||||||
|
|
||||||
|
if (this->highlightedMessage_ == layout)
|
||||||
|
{
|
||||||
|
painter.fillRect(
|
||||||
|
0, y, layout->getWidth(), layout->getHeight(),
|
||||||
|
this->highlightAnimation_.currentValue().value<QColor>());
|
||||||
|
if (this->highlightAnimation_.state() == QVariantAnimation::Stopped)
|
||||||
|
{
|
||||||
|
this->highlightedMessage_ = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
y += layout->getHeight();
|
y += layout->getHeight();
|
||||||
|
|
||||||
end = layout;
|
end = layout;
|
||||||
|
@ -2070,6 +2192,46 @@ void ChannelView::addMessageContextMenuItems(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isSearch = this->context_ == Context::Search;
|
||||||
|
bool isReplyOrUserCard = (this->context_ == Context::ReplyThread ||
|
||||||
|
this->context_ == Context::UserCard) &&
|
||||||
|
this->split_;
|
||||||
|
bool isMentions =
|
||||||
|
this->channel()->getType() == Channel::Type::TwitchMentions;
|
||||||
|
if (isSearch || isMentions || isReplyOrUserCard)
|
||||||
|
{
|
||||||
|
const auto &messagePtr = layout->getMessagePtr();
|
||||||
|
menu.addAction("Go to message", [this, &messagePtr, isSearch,
|
||||||
|
isMentions, isReplyOrUserCard] {
|
||||||
|
if (isSearch)
|
||||||
|
{
|
||||||
|
if (const auto &search =
|
||||||
|
dynamic_cast<SearchPopup *>(this->parentWidget()))
|
||||||
|
{
|
||||||
|
search->goToMessage(messagePtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isMentions)
|
||||||
|
{
|
||||||
|
getApp()->windows->scrollToMessage(messagePtr);
|
||||||
|
}
|
||||||
|
else if (isReplyOrUserCard)
|
||||||
|
{
|
||||||
|
// If the thread is in the mentions channel,
|
||||||
|
// we need to find the original split.
|
||||||
|
if (this->split_->getChannel()->getType() ==
|
||||||
|
Channel::Type::TwitchMentions)
|
||||||
|
{
|
||||||
|
getApp()->windows->scrollToMessage(messagePtr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->split_->getChannelView().scrollToMessage(messagePtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChannelView::addTwitchLinkContextMenuItems(
|
void ChannelView::addTwitchLinkContextMenuItems(
|
||||||
|
@ -2321,6 +2483,30 @@ void ChannelView::showUserInfoPopup(const QString &userName,
|
||||||
userPopup->show();
|
userPopup->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ChannelView::mayContainMessage(const MessagePtr &message)
|
||||||
|
{
|
||||||
|
switch (this->channel()->getType())
|
||||||
|
{
|
||||||
|
case Channel::Type::Direct:
|
||||||
|
case Channel::Type::Twitch:
|
||||||
|
case Channel::Type::TwitchWatching:
|
||||||
|
case Channel::Type::Irc:
|
||||||
|
return this->channel()->getName() == message->channelName;
|
||||||
|
case Channel::Type::TwitchWhispers:
|
||||||
|
return message->flags.has(MessageFlag::Whisper);
|
||||||
|
case Channel::Type::TwitchMentions:
|
||||||
|
return message->flags.has(MessageFlag::Highlighted);
|
||||||
|
case Channel::Type::TwitchLive:
|
||||||
|
return message->flags.has(MessageFlag::System);
|
||||||
|
case Channel::Type::TwitchEnd: // TODO: not used?
|
||||||
|
case Channel::Type::None: // Unspecific
|
||||||
|
case Channel::Type::Misc: // Unspecific
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return true; // unreachable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ChannelView::handleLinkClick(QMouseEvent *event, const Link &link,
|
void ChannelView::handleLinkClick(QMouseEvent *event, const Link &link,
|
||||||
MessageLayout *layout)
|
MessageLayout *layout)
|
||||||
{
|
{
|
||||||
|
@ -2442,6 +2628,21 @@ void ChannelView::handleLinkClick(QMouseEvent *event, const Link &link,
|
||||||
this->showReplyThreadPopup(layout->getMessagePtr());
|
this->showReplyThreadPopup(layout->getMessagePtr());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case Link::JumpToMessage: {
|
||||||
|
if (this->context_ == Context::Search)
|
||||||
|
{
|
||||||
|
if (auto search =
|
||||||
|
dynamic_cast<SearchPopup *>(this->parentWidget()))
|
||||||
|
{
|
||||||
|
search->goToMessageId(link.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->scrollToMessageId(link.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:;
|
default:;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <QPaintEvent>
|
#include <QPaintEvent>
|
||||||
#include <QScroller>
|
#include <QScroller>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
#include <QVariantAnimation>
|
||||||
#include <QWheelEvent>
|
#include <QWheelEvent>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <pajlada/signals/signal.hpp>
|
#include <pajlada/signals/signal.hpp>
|
||||||
|
@ -83,6 +84,17 @@ public:
|
||||||
const boost::optional<MessageElementFlags> &getOverrideFlags() const;
|
const boost::optional<MessageElementFlags> &getOverrideFlags() const;
|
||||||
void updateLastReadMessage();
|
void updateLastReadMessage();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to scroll to a message in this channel.
|
||||||
|
* @return <code>true</code> if the message was found and highlighted.
|
||||||
|
*/
|
||||||
|
bool scrollToMessage(const MessagePtr &message);
|
||||||
|
/**
|
||||||
|
* Attempts to scroll to a message id in this channel.
|
||||||
|
* @return <code>true</code> if the message was found and highlighted.
|
||||||
|
*/
|
||||||
|
bool scrollToMessageId(const QString &id);
|
||||||
|
|
||||||
/// Pausing
|
/// Pausing
|
||||||
bool pausable() const;
|
bool pausable() const;
|
||||||
void setPausable(bool value);
|
void setPausable(bool value);
|
||||||
|
@ -119,6 +131,13 @@ public:
|
||||||
void showUserInfoPopup(const QString &userName,
|
void showUserInfoPopup(const QString &userName,
|
||||||
QString alternativePopoutChannel = QString());
|
QString alternativePopoutChannel = QString());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This method is meant to be used when filtering out channels.
|
||||||
|
* It <b>must</b> return true if a message belongs in this channel.
|
||||||
|
* It <b>might</b> return true if a message doesn't belong in this channel.
|
||||||
|
*/
|
||||||
|
bool mayContainMessage(const MessagePtr &message);
|
||||||
|
|
||||||
pajlada::Signals::Signal<QMouseEvent *> mouseDown;
|
pajlada::Signals::Signal<QMouseEvent *> mouseDown;
|
||||||
pajlada::Signals::NoArgSignal selectionChanged;
|
pajlada::Signals::NoArgSignal selectionChanged;
|
||||||
pajlada::Signals::Signal<HighlightState> tabHighlightRequested;
|
pajlada::Signals::Signal<HighlightState> tabHighlightRequested;
|
||||||
|
@ -208,6 +227,14 @@ private:
|
||||||
void enableScrolling(const QPointF &scrollStart);
|
void enableScrolling(const QPointF &scrollStart);
|
||||||
void disableScrolling();
|
void disableScrolling();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scrolls to a message layout that must be from this view.
|
||||||
|
*
|
||||||
|
* @param layout Must be from this channel.
|
||||||
|
* @param messageIdx Must be an index into this channel.
|
||||||
|
*/
|
||||||
|
void scrollToMessageLayout(MessageLayout *layout, size_t messageIdx);
|
||||||
|
|
||||||
void setInputReply(const MessagePtr &message);
|
void setInputReply(const MessagePtr &message);
|
||||||
void showReplyThreadPopup(const MessagePtr &message);
|
void showReplyThreadPopup(const MessagePtr &message);
|
||||||
bool canReplyToMessages() const;
|
bool canReplyToMessages() const;
|
||||||
|
@ -241,6 +268,7 @@ private:
|
||||||
|
|
||||||
Scrollbar *scrollBar_;
|
Scrollbar *scrollBar_;
|
||||||
EffectLabel *goToBottom_;
|
EffectLabel *goToBottom_;
|
||||||
|
bool showScrollBar_ = false;
|
||||||
|
|
||||||
FilterSetPtr channelFilters_;
|
FilterSetPtr channelFilters_;
|
||||||
|
|
||||||
|
@ -272,6 +300,11 @@ private:
|
||||||
QPointF currentMousePosition_;
|
QPointF currentMousePosition_;
|
||||||
QTimer scrollTimer_;
|
QTimer scrollTimer_;
|
||||||
|
|
||||||
|
// We're only interested in the pointer, not the contents
|
||||||
|
MessageLayout *highlightedMessage_;
|
||||||
|
QVariantAnimation highlightAnimation_;
|
||||||
|
void setupHighlightAnimationColors();
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
QCursor neutral;
|
QCursor neutral;
|
||||||
QCursor up;
|
QCursor up;
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "messages/search/MessageFlagsPredicate.hpp"
|
#include "messages/search/MessageFlagsPredicate.hpp"
|
||||||
#include "messages/search/RegexPredicate.hpp"
|
#include "messages/search/RegexPredicate.hpp"
|
||||||
#include "messages/search/SubstringPredicate.hpp"
|
#include "messages/search/SubstringPredicate.hpp"
|
||||||
|
#include "singletons/WindowManager.hpp"
|
||||||
#include "widgets/helper/ChannelView.hpp"
|
#include "widgets/helper/ChannelView.hpp"
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
@ -106,6 +107,34 @@ void SearchPopup::addChannel(ChannelView &channel)
|
||||||
this->updateWindowTitle();
|
this->updateWindowTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SearchPopup::goToMessage(const MessagePtr &message)
|
||||||
|
{
|
||||||
|
for (const auto &view : this->searchChannels_)
|
||||||
|
{
|
||||||
|
if (view.get().channel()->getType() == Channel::Type::TwitchMentions)
|
||||||
|
{
|
||||||
|
getApp()->windows->scrollToMessage(message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (view.get().scrollToMessage(message))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SearchPopup::goToMessageId(const QString &messageId)
|
||||||
|
{
|
||||||
|
for (const auto &view : this->searchChannels_)
|
||||||
|
{
|
||||||
|
if (view.get().scrollToMessageId(messageId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SearchPopup::updateWindowTitle()
|
void SearchPopup::updateWindowTitle()
|
||||||
{
|
{
|
||||||
QString historyName;
|
QString historyName;
|
||||||
|
|
|
@ -19,6 +19,14 @@ public:
|
||||||
SearchPopup(QWidget *parent, Split *split = nullptr);
|
SearchPopup(QWidget *parent, Split *split = nullptr);
|
||||||
|
|
||||||
virtual void addChannel(ChannelView &channel);
|
virtual void addChannel(ChannelView &channel);
|
||||||
|
void goToMessage(const MessagePtr &message);
|
||||||
|
/**
|
||||||
|
* This method should only be used for searches that
|
||||||
|
* don't include a mentions channel,
|
||||||
|
* since it will only search in the opened channels (not globally).
|
||||||
|
* @param messageId
|
||||||
|
*/
|
||||||
|
void goToMessageId(const QString &messageId);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void updateWindowTitle();
|
virtual void updateWindowTitle();
|
||||||
|
|
Loading…
Reference in a new issue