diff --git a/resources/images/collapse.png b/resources/images/collapse.png
new file mode 100644
index 000000000..b181ccbf9
Binary files /dev/null and b/resources/images/collapse.png differ
diff --git a/resources/resources.qrc b/resources/resources.qrc
index 511243501..6ffc4de55 100644
--- a/resources/resources.qrc
+++ b/resources/resources.qrc
@@ -33,6 +33,7 @@
images/VSO_Link_blue_16x.png
sounds/ping2.wav
images/subscriber.png
+ images/collapse.png
qt.conf
diff --git a/src/messages/message.cpp b/src/messages/message.cpp
index bdd136a23..1a47eb28a 100644
--- a/src/messages/message.cpp
+++ b/src/messages/message.cpp
@@ -61,6 +61,16 @@ const QString &Message::getId() const
return this->id;
}
+bool Message::getCollapsedDefault() const
+{
+ return this->collapsedDefault;
+}
+
+void Message::setCollapsedDefault(bool value)
+{
+ this->collapsedDefault = value;
+}
+
namespace {
void AddCurrentTimestamp(Message *message)
diff --git a/src/messages/message.hpp b/src/messages/message.hpp
index b6600549d..aa036749b 100644
--- a/src/messages/message.hpp
+++ b/src/messages/message.hpp
@@ -27,6 +27,8 @@ public:
std::vector &getWords();
bool isDisabled() const;
const QString &getId() const;
+ bool getCollapsedDefault() const;
+ void setCollapsedDefault(bool value);
QString loginName;
QString displayName;
@@ -57,6 +59,9 @@ private:
QString timeoutUser = "";
int timeoutCount = 0;
bool disabled = false;
+
+ bool collapsedDefault = false;
+
std::chrono::time_point parseTime;
QString content;
diff --git a/src/messages/messageref.cpp b/src/messages/messageref.cpp
index 115a918e6..5a632a493 100644
--- a/src/messages/messageref.cpp
+++ b/src/messages/messageref.cpp
@@ -4,10 +4,10 @@
#include
-#define MARGIN_LEFT 8
-#define MARGIN_RIGHT 8
-#define MARGIN_TOP 4
-#define MARGIN_BOTTOM 4
+#define MARGIN_LEFT (int)(8 * this->dpiMultiplier)
+#define MARGIN_RIGHT (int)(8 * this->dpiMultiplier)
+#define MARGIN_TOP (int)(4 * this->dpiMultiplier)
+#define MARGIN_BOTTOM (int)(4 * this->dpiMultiplier)
using namespace chatterino::messages;
@@ -17,6 +17,7 @@ namespace messages {
MessageRef::MessageRef(SharedMessage _message)
: message(_message)
, wordParts()
+ , collapsed(_message->getCollapsedDefault())
{
}
@@ -59,9 +60,9 @@ bool MessageRef::layout(int width, float dpiMultiplyer)
this->currentWordTypes = SettingsManager::getInstance().getWordTypeMask();
// check if dpi changed
- bool dpiChanged = this->dpiMultiplyer != dpiMultiplyer;
+ bool dpiChanged = this->dpiMultiplier != dpiMultiplyer;
layoutRequired |= dpiChanged;
- this->dpiMultiplyer = dpiMultiplyer;
+ this->dpiMultiplier = dpiMultiplyer;
imagesChanged |= dpiChanged;
textChanged |= dpiChanged;
@@ -105,9 +106,13 @@ void MessageRef::actuallyLayout(int width)
int lineNumber = 0;
int lineStart = 0;
int lineHeight = 0;
+ int firstLineHeight = -1;
bool first = true;
uint32_t flags = settings.getWordTypeMask();
+ if (this->collapsed) {
+ flags |= Word::Collapsed;
+ }
// loop throught all the words and add them when a line is full
for (auto it = this->message->getWords().begin(); it != this->message->getWords().end(); ++it) {
@@ -132,7 +137,7 @@ void MessageRef::actuallyLayout(int width)
// word wrapping
if (word.isText() && word.getWidth() + MARGIN_LEFT > right) {
// align and end the current line
- alignWordParts(lineStart, lineHeight, width);
+ alignWordParts(lineStart, lineHeight, width, firstLineHeight);
y += lineHeight;
int currentPartStart = 0;
@@ -187,7 +192,7 @@ void MessageRef::actuallyLayout(int width)
// doesn't fit in the line
else {
// align and end the current line
- alignWordParts(lineStart, lineHeight, width);
+ alignWordParts(lineStart, lineHeight, width, firstLineHeight);
y += lineHeight;
@@ -206,11 +211,19 @@ void MessageRef::actuallyLayout(int width)
}
// align and end the current line
- alignWordParts(lineStart, lineHeight, width);
+ alignWordParts(lineStart, lineHeight, width, firstLineHeight);
+
+ this->collapsedHeight = firstLineHeight == -1 ? (int)(24 * dpiMultiplier)
+ : firstLineHeight + MARGIN_TOP + MARGIN_BOTTOM;
// update height
int oldHeight = this->height;
- this->height = y + lineHeight + MARGIN_BOTTOM;
+
+ if (this->isCollapsed()) {
+ this->height = this->collapsedHeight;
+ } else {
+ this->height = y + lineHeight + MARGIN_BOTTOM;
+ }
// invalidate buffer if height changed
if (oldHeight != this->height) {
@@ -227,8 +240,8 @@ void MessageRef::updateTextSizes()
continue;
QFontMetrics &metrics = word.getFontMetrics();
- word.setSize((int)(metrics.width(word.getText()) * this->dpiMultiplyer),
- (int)(metrics.height() * this->dpiMultiplyer));
+ word.setSize((int)(metrics.width(word.getText()) * this->dpiMultiplier),
+ (int)(metrics.height() * this->dpiMultiplier));
}
}
@@ -236,7 +249,7 @@ void MessageRef::updateImageSizes()
{
const int mediumTextLineHeight =
FontManager::getInstance().getFontMetrics(FontManager::Medium).height();
- const qreal emoteScale = SettingsManager::getInstance().emoteScale.get() * this->dpiMultiplyer;
+ const qreal emoteScale = SettingsManager::getInstance().emoteScale.get() * this->dpiMultiplier;
const bool scaleEmotesByLineHeight = SettingsManager::getInstance().scaleEmotesByLineHeight;
for (auto &word : this->message->getWords()) {
@@ -262,8 +275,12 @@ const std::vector &MessageRef::getWordParts() const
return this->wordParts;
}
-void MessageRef::alignWordParts(int lineStart, int lineHeight, int width)
+void MessageRef::alignWordParts(int lineStart, int lineHeight, int width, int &firstLineHeight)
{
+ if (firstLineHeight == -1) {
+ firstLineHeight = lineHeight;
+ }
+
int xOffset = 0;
if (this->message->centered && this->wordParts.size() > 0) {
@@ -399,5 +416,22 @@ int MessageRef::getSelectionIndex(QPoint position)
return index;
}
+bool MessageRef::isCollapsed() const
+{
+ return this->collapsed;
+}
+
+void MessageRef::setCollapsed(bool value)
+{
+ if (this->collapsed != value) {
+ this->currentLayoutWidth = 0;
+ this->collapsed = value;
+ }
+}
+
+int MessageRef::getCollapsedHeight() const
+{
+ return this->collapsedHeight;
+}
} // namespace messages
} // namespace chatterino
diff --git a/src/messages/messageref.hpp b/src/messages/messageref.hpp
index 76032761e..48c0ea7cd 100644
--- a/src/messages/messageref.hpp
+++ b/src/messages/messageref.hpp
@@ -22,7 +22,7 @@ public:
Message *getMessage();
int getHeight() const;
- bool layout(int width, float dpiMultiplyer);
+ bool layout(int width, float dpiMultiplier);
const std::vector &getWordParts() const;
@@ -33,6 +33,10 @@ public:
int getLastCharacterIndex() const;
int getSelectionIndex(QPoint position);
+ bool isCollapsed() const;
+ void setCollapsed(bool value);
+ int getCollapsedHeight() const;
+
private:
// variables
SharedMessage message;
@@ -43,13 +47,16 @@ private:
int currentLayoutWidth = -1;
int fontGeneration = -1;
int emoteGeneration = -1;
- float dpiMultiplyer = -1;
+ float dpiMultiplier = -1;
Word::Type currentWordTypes = Word::None;
+ bool collapsed;
+ int collapsedHeight = 32;
+
// methods
void actuallyLayout(int width);
- void alignWordParts(int lineStart, int lineHeight, int width);
+ void alignWordParts(int lineStart, int lineHeight, int width, int &firstLineHeight);
void updateTextSizes();
void updateImageSizes();
};
diff --git a/src/messages/word.hpp b/src/messages/word.hpp
index 9c8075d0d..caf6878e8 100644
--- a/src/messages/word.hpp
+++ b/src/messages/word.hpp
@@ -80,6 +80,9 @@ public:
AlwaysShow = (1 << 25),
+ // used in the ChannelView class to make the collapse buttons visible if needed
+ Collapsed = (1 << 26),
+
Default = TimestampNoSeconds | Badges | Username | BitsStatic | FfzEmoteImage |
BttvEmoteImage | BttvGifEmoteImage | TwitchEmoteImage | BitsAmount | Text |
ButtonBan | ButtonTimeout | AlwaysShow
diff --git a/src/resources.cpp b/src/resources.cpp
index 350df4d3f..2f620390a 100644
--- a/src/resources.cpp
+++ b/src/resources.cpp
@@ -26,6 +26,7 @@ Resources::Resources()
, badgePremium(lli(":/images/twitchprime_bg.png"))
, badgeVerified(lli(":/images/verified.png", 0.25))
, badgeSubscriber(lli(":/images/subscriber.png", 0.25))
+ , badgeCollapsed(lli(":/images/collapse.png"))
, cheerBadge100000(lli(":/images/cheer100000"))
, cheerBadge10000(lli(":/images/cheer10000"))
, cheerBadge5000(lli(":/images/cheer5000"))
diff --git a/src/resources.hpp b/src/resources.hpp
index ac918ec8a..6288d5a69 100644
--- a/src/resources.hpp
+++ b/src/resources.hpp
@@ -22,6 +22,7 @@ public:
messages::LazyLoadedImage *badgePremium;
messages::LazyLoadedImage *badgeVerified;
messages::LazyLoadedImage *badgeSubscriber;
+ messages::LazyLoadedImage *badgeCollapsed;
messages::LazyLoadedImage *cheerBadge100000;
messages::LazyLoadedImage *cheerBadge10000;
diff --git a/src/twitch/twitchmessagebuilder.cpp b/src/twitch/twitchmessagebuilder.cpp
index 1acd3be77..41295b890 100644
--- a/src/twitch/twitchmessagebuilder.cpp
+++ b/src/twitch/twitchmessagebuilder.cpp
@@ -41,6 +41,10 @@ SharedMessage TwitchMessageBuilder::parse()
this->parseUsername();
+ // this->message->setCollapsedDefault(true);
+ // this->appendWord(Word(this->resources.badgeCollapsed, Word::Collapsed, QString(),
+ // QString()));
+
// The timestamp is always appended to the builder
// Whether or not will be rendered is decided/checked later
this->appendTimestamp();
diff --git a/src/widgets/helper/channelview.cpp b/src/widgets/helper/channelview.cpp
index 89c13909c..4e7e98cdf 100644
--- a/src/widgets/helper/channelview.cpp
+++ b/src/widgets/helper/channelview.cpp
@@ -729,11 +729,19 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event)
QPoint relativePos;
int messageIndex;
+ // no message under cursor
if (!tryGetMessageAt(event->pos(), message, relativePos, messageIndex)) {
- setCursor(Qt::ArrowCursor);
+ this->setCursor(Qt::ArrowCursor);
return;
}
+ // message under cursor is collapsed
+ if (message->isCollapsed()) {
+ this->setCursor(Qt::PointingHandCursor);
+ return;
+ }
+
+ // is selecting
if (this->selecting) {
int index = message->getSelectionIndex(relativePos);
@@ -742,16 +750,18 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event)
this->repaint();
}
+ // check if word underneath cursor
const messages::Word *hoverWord;
if ((hoverWord = message->tryGetWordPart(relativePos)) == nullptr) {
- setCursor(Qt::ArrowCursor);
+ this->setCursor(Qt::ArrowCursor);
return;
}
+ // check if word has a link
if (hoverWord->getLink().isValid()) {
- setCursor(Qt::PointingHandCursor);
+ this->setCursor(Qt::PointingHandCursor);
} else {
- setCursor(Qt::ArrowCursor);
+ this->setCursor(Qt::ArrowCursor);
}
}
@@ -787,6 +797,11 @@ void ChannelView::mousePressEvent(QMouseEvent *event)
return;
}
+ // check if message is collapsed
+ if (message->isCollapsed()) {
+ return;
+ }
+
int index = message->getSelectionIndex(relativePos);
auto selectionItem = SelectionItem(messageIndex, index);
@@ -831,6 +846,13 @@ void ChannelView::mouseReleaseEvent(QMouseEvent *event)
return;
}
+ // message under cursor is collapsed
+ if (message->isCollapsed()) {
+ message->setCollapsed(false);
+ this->layoutMessages();
+ return;
+ }
+
const messages::Word *hoverWord;
if ((hoverWord = message->tryGetWordPart(relativePos)) == nullptr) {