Refactor SplitHeader class (#4276)

* Flatten static functions in anonymous namespace

* SplitHeader ctor: Rename param

* Header: Remove unnecessary `virtual`s

* auto ptr where possible

* Add curly braces

* Comment twitch room modes

* Treat roomModes->slowMode as an integer

* Remove unused `this` from lambdas

* Add `unsigned int` overload for localizeNumbers

* Move thumbnail max age to a constexpr static & set explicit types

* Explicitly use `QObject::connect`

* Use `empty()` instead of `size()`

* Name unused parameters

* Move moderation action refreshing logic from SplitHeader to Split
This commit is contained in:
pajlada 2022-12-31 12:56:47 +01:00 committed by GitHub
parent 6c1bd1837e
commit 5fc170ba4b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 248 additions and 186 deletions

View file

@ -75,7 +75,21 @@ public:
bool submode = false;
bool r9k = false;
bool emoteOnly = false;
/**
* @brief Number of minutes required for users to be followed before typing in chat
*
* Special cases:
* -1 = follower mode off
* 0 = follower mode on, no time requirement
**/
int followerOnly = -1;
/**
* @brief Number of seconds required to wait before typing emotes
*
* 0 = slow mode off
**/
int slowMode = 0;
};

View file

@ -168,6 +168,12 @@ QString localizeNumbers(const int &number)
return locale.toString(number);
}
QString localizeNumbers(unsigned int number)
{
QLocale locale;
return locale.toString(number);
}
QString kFormatNumbers(const int &number)
{
return QString("%1K").arg(number / 1000);

View file

@ -72,6 +72,7 @@ QString formatRichNamedLink(const QString &url, const QString &name,
QString shortenString(const QString &str, unsigned maxWidth = 50);
QString localizeNumbers(const int &number);
QString localizeNumbers(unsigned int number);
QString kFormatNumbers(const int &number);

View file

@ -198,6 +198,12 @@ Split::Split(QWidget *parent)
this->setSizePolicy(QSizePolicy::MinimumExpanding,
QSizePolicy::MinimumExpanding);
// update moderation button when items changed
this->signalHolder_.managedConnect(
getSettings()->moderationActions.delayedItemsChanged, [this] {
this->refreshModerationMode();
});
this->signalHolder_.managedConnect(
modifierStatusChanged, [this](Qt::KeyboardModifiers status) {
if ((status ==
@ -632,6 +638,12 @@ void Split::joinChannelInNewTab(ChannelPtr channel)
container->insertSplit(split);
}
void Split::refreshModerationMode()
{
this->header_->updateModerationModeIcon();
this->view_->queueLayout();
}
void Split::openChannelInBrowserPlayer(ChannelPtr channel)
{
if (auto twitchChannel = dynamic_cast<TwitchChannel *>(channel.get()))
@ -723,8 +735,7 @@ void Split::setChannel(IndirectChannel newChannel)
void Split::setModerationMode(bool value)
{
this->moderationMode_ = value;
this->header_->updateModerationModeIcon();
this->view_->queueLayout();
this->refreshModerationMode();
}
bool Split::getModerationMode() const

View file

@ -130,6 +130,14 @@ private:
*/
void joinChannelInNewTab(ChannelPtr channel);
/**
* @brief Refresh moderation mode layouts/buttons
*
* Should be called after after the moderation mode is changed or
* moderation actions have been changed
**/
void refreshModerationMode();
IndirectChannel channel_;
bool moderationMode_{};

View file

@ -40,157 +40,192 @@
# include "widgets/StreamView.hpp"
#endif
namespace chatterino {
namespace {
auto formatRoomMode(TwitchChannel &channel) -> QString
using namespace chatterino;
// 5 minutes
constexpr const uint64_t THUMBNAIL_MAX_AGE_MS = 5ULL * 60 * 1000;
auto formatRoomMode(TwitchChannel &channel) -> QString
{
QString text;
{
QString text;
auto modes = channel.accessRoomModes();
if (modes->r9k)
{
auto modes = channel.accessRoomModes();
if (modes->r9k)
text += "r9k, ";
if (modes->slowMode)
text +=
QString("slow(%1), ").arg(localizeNumbers(modes->slowMode));
if (modes->emoteOnly)
text += "emote, ";
if (modes->submode)
text += "sub, ";
if (modes->followerOnly != -1)
text += "r9k, ";
}
if (modes->slowMode > 0)
{
text += QString("slow(%1), ").arg(localizeNumbers(modes->slowMode));
}
if (modes->emoteOnly)
{
text += "emote, ";
}
if (modes->submode)
{
text += "sub, ";
}
if (modes->followerOnly != -1)
{
if (modes->followerOnly != 0)
{
if (modes->followerOnly != 0)
{
text += QString("follow(%1m), ")
.arg(localizeNumbers(modes->followerOnly));
}
else
{
text += QString("follow, ");
}
text += QString("follow(%1m), ")
.arg(localizeNumbers(modes->followerOnly));
}
else
{
text += QString("follow, ");
}
}
}
if (text.length() > 2)
if (text.length() > 2)
{
text = text.mid(0, text.size() - 2);
}
if (!text.isEmpty())
{
static QRegularExpression commaReplacement("^(.+?, .+?,) (.+)$");
auto match = commaReplacement.match(text);
if (match.hasMatch())
{
text = text.mid(0, text.size() - 2);
text = match.captured(1) + '\n' + match.captured(2);
}
}
if (text.isEmpty() && channel.hasModRights())
{
return "none";
}
return text;
}
auto formatTooltip(const TwitchChannel::StreamStatus &s, QString thumbnail)
{
auto title = [&s]() -> QString {
if (s.title.isEmpty())
{
return QStringLiteral("");
}
if (!text.isEmpty())
{
static QRegularExpression commaReplacement("^(.+?, .+?,) (.+)$");
return s.title.toHtmlEscaped() + "<br><br>";
}();
auto match = commaReplacement.match(text);
if (match.hasMatch())
text = match.captured(1) + '\n' + match.captured(2);
auto tooltip = [&thumbnail]() -> QString {
if (getSettings()->thumbnailSizeStream.getValue() == 0)
{
return QStringLiteral("");
}
if (text.isEmpty() && channel.hasModRights())
return "none";
return text;
}
auto formatTooltip(const TwitchChannel::StreamStatus &s, QString thumbnail)
{
auto title = [&s]() -> QString {
if (s.title.isEmpty())
{
return QStringLiteral("");
}
return s.title.toHtmlEscaped() + "<br><br>";
}();
auto tooltip = [&thumbnail]() -> QString {
if (getSettings()->thumbnailSizeStream.getValue() == 0)
{
return QStringLiteral("");
}
if (thumbnail.isEmpty())
{
return QStringLiteral("Couldn't fetch thumbnail<br>");
}
return "<img src=\"data:image/jpg;base64, " + thumbnail + "\"><br>";
}();
auto game = [&s]() -> QString {
if (s.game.isEmpty())
{
return QStringLiteral("");
}
return s.game.toHtmlEscaped() + "<br>";
}();
auto extraStreamData = [&s]() -> QString {
if (isInStreamerMode() &&
getSettings()->streamerModeHideViewerCountAndDuration)
{
return QStringLiteral(
"<span style=\"color: #808892;\">&lt;Streamer "
"Mode&gt;</span>");
}
return QString("%1 for %2 with %3 viewers")
.arg(s.rerun ? "Vod-casting" : "Live")
.arg(s.uptime)
.arg(localizeNumbers(s.viewerCount));
}();
return QString("<p style=\"text-align: center;\">" + //
title + //
tooltip + //
game + //
extraStreamData + //
"</p>" //
);
}
auto formatOfflineTooltip(const TwitchChannel::StreamStatus &s)
{
return QString("<p style=\"text-align: center;\">Offline<br>%1</p>")
.arg(s.title.toHtmlEscaped());
}
auto formatTitle(const TwitchChannel::StreamStatus &s, Settings &settings)
{
auto title = QString();
// live
if (s.rerun)
title += " (rerun)";
else if (s.streamType.isEmpty())
title += " (" + s.streamType + ")";
else
title += " (live)";
// description
if (settings.headerUptime)
title += " - " + s.uptime;
if (settings.headerViewerCount)
title += " - " + localizeNumbers(s.viewerCount);
if (settings.headerGame && !s.game.isEmpty())
title += " - " + s.game;
if (settings.headerStreamTitle && !s.title.isEmpty())
if (thumbnail.isEmpty())
{
title += " - " + s.title.simplified();
return QStringLiteral("Couldn't fetch thumbnail<br>");
}
return title;
}
auto distance(QPoint a, QPoint b)
{
auto x = std::abs(a.x() - b.x());
auto y = std::abs(a.y() - b.y());
return "<img src=\"data:image/jpg;base64, " + thumbnail + "\"><br>";
}();
return std::sqrt(x * x + y * y);
auto game = [&s]() -> QString {
if (s.game.isEmpty())
{
return QStringLiteral("");
}
return s.game.toHtmlEscaped() + "<br>";
}();
auto extraStreamData = [&s]() -> QString {
if (isInStreamerMode() &&
getSettings()->streamerModeHideViewerCountAndDuration)
{
return QStringLiteral(
"<span style=\"color: #808892;\">&lt;Streamer "
"Mode&gt;</span>");
}
return QString("%1 for %2 with %3 viewers")
.arg(s.rerun ? "Vod-casting" : "Live")
.arg(s.uptime)
.arg(localizeNumbers(s.viewerCount));
}();
return QString("<p style=\"text-align: center;\">" + //
title + //
tooltip + //
game + //
extraStreamData + //
"</p>" //
);
}
auto formatOfflineTooltip(const TwitchChannel::StreamStatus &s)
{
return QString("<p style=\"text-align: center;\">Offline<br>%1</p>")
.arg(s.title.toHtmlEscaped());
}
auto formatTitle(const TwitchChannel::StreamStatus &s, Settings &settings)
{
auto title = QString();
// live
if (s.rerun)
{
title += " (rerun)";
}
else if (s.streamType.isEmpty())
{
title += " (" + s.streamType + ")";
}
else
{
title += " (live)";
}
// description
if (settings.headerUptime)
{
title += " - " + s.uptime;
}
if (settings.headerViewerCount)
{
title += " - " + localizeNumbers(s.viewerCount);
}
if (settings.headerGame && !s.game.isEmpty())
{
title += " - " + s.game;
}
if (settings.headerStreamTitle && !s.title.isEmpty())
{
title += " - " + s.title.simplified();
}
return title;
}
auto distance(QPoint a, QPoint b)
{
auto x = std::abs(a.x() - b.x());
auto y = std::abs(a.y() - b.y());
return std::sqrt(x * x + y * y);
}
} // namespace
SplitHeader::SplitHeader(Split *_split)
: BaseWidget(_split)
, split_(_split)
namespace chatterino {
SplitHeader::SplitHeader(Split *split)
: BaseWidget(split)
, split_(split)
{
this->initializeLayout();
@ -225,7 +260,7 @@ SplitHeader::SplitHeader(Split *_split)
void SplitHeader::initializeLayout()
{
auto layout = makeLayout<QHBoxLayout>({
auto *layout = makeLayout<QHBoxLayout>({
// space
makeWidget<BaseWidget>([](auto w) {
w->setScaleIndependantSize(8, 4);
@ -307,24 +342,9 @@ void SplitHeader::initializeLayout()
}),
});
// update moderation button when items changed
this->managedConnections_.managedConnect(
getSettings()->moderationActions.delayedItemsChanged, [this] {
if (getSettings()->moderationActions.empty())
{
if (this->split_->getModerationMode())
this->split_->setModerationMode(true);
}
else
{
if (this->split_->getModerationMode())
this->split_->setModerationMode(true);
}
});
getSettings()->customURIScheme.connect(
[this] {
if (const auto drop = this->dropdownButton_)
if (auto *const drop = this->dropdownButton_)
{
drop->setMenu(this->createMainMenu());
}
@ -444,7 +464,7 @@ std::unique_ptr<QMenu> SplitHeader::createMainMenu()
{
// "How to..." sub menu
auto subMenu = new QMenu("How to...", this);
auto *subMenu = new QMenu("How to...", this);
subMenu->addAction("move split", this->split_, &Split::explainMoving);
subMenu->addAction("add/split", this->split_, &Split::explainSplitting);
menu->addMenu(subMenu);
@ -453,7 +473,7 @@ std::unique_ptr<QMenu> SplitHeader::createMainMenu()
menu->addSeparator();
// sub menu
auto moreMenu = new QMenu("More", this);
auto *moreMenu = new QMenu("More", this);
auto modModeSeq = h->getDisplaySequence(HotkeyCategory::Split,
"setModerationMode", {{"toggle"}});
@ -472,14 +492,14 @@ std::unique_ptr<QMenu> SplitHeader::createMainMenu()
if (this->split_->getChannel()->getType() == Channel::Type::TwitchMentions)
{
auto action = new QAction(this);
auto *action = new QAction(this);
action->setText("Enable /mention tab highlights");
action->setCheckable(true);
QObject::connect(moreMenu, &QMenu::aboutToShow, this, [action, this]() {
QObject::connect(moreMenu, &QMenu::aboutToShow, this, [action]() {
action->setChecked(getSettings()->highlightMentions);
});
action->connect(action, &QAction::triggered, this, [this]() {
QObject::connect(action, &QAction::triggered, this, []() {
getSettings()->highlightMentions =
!getSettings()->highlightMentions;
});
@ -495,7 +515,7 @@ std::unique_ptr<QMenu> SplitHeader::createMainMenu()
moreMenu->addAction("Subscribe", this->split_, &Split::openSubPage);
auto action = new QAction(this);
auto *action = new QAction(this);
action->setText("Notify when live");
action->setCheckable(true);
@ -513,7 +533,7 @@ std::unique_ptr<QMenu> SplitHeader::createMainMenu()
action->setChecked(getApp()->notifications->isChannelNotified(
this->split_->getChannel()->getName(), Platform::Twitch));
});
action->connect(action, &QAction::triggered, this, [this]() {
QObject::connect(action, &QAction::triggered, this, [this]() {
getApp()->notifications->updateChannelNotification(
this->split_->getChannel()->getName(), Platform::Twitch);
});
@ -523,7 +543,7 @@ std::unique_ptr<QMenu> SplitHeader::createMainMenu()
if (twitchChannel)
{
auto action = new QAction(this);
auto *action = new QAction(this);
action->setText("Mute highlight sound");
action->setCheckable(true);
@ -531,7 +551,7 @@ std::unique_ptr<QMenu> SplitHeader::createMainMenu()
action->setChecked(getSettings()->isMutedChannel(
this->split_->getChannel()->getName()));
});
action->connect(action, &QAction::triggered, this, [this]() {
QObject::connect(action, &QAction::triggered, this, [this]() {
getSettings()->toggleMutedChannel(
this->split_->getChannel()->getName());
});
@ -555,11 +575,11 @@ std::unique_ptr<QMenu> SplitHeader::createChatModeMenu()
{
auto menu = std::make_unique<QMenu>();
auto setSub = new QAction("Subscriber only", this);
auto setEmote = new QAction("Emote only", this);
auto setSlow = new QAction("Slow", this);
auto setR9k = new QAction("R9K", this);
auto setFollowers = new QAction("Followers only", this);
auto *setSub = new QAction("Subscriber only", this);
auto *setEmote = new QAction("Emote only", this);
auto *setSlow = new QAction("Slow", this);
auto *setR9k = new QAction("R9K", this);
auto *setFollowers = new QAction("Followers only", this);
setFollowers->setCheckable(true);
setSub->setCheckable(true);
@ -576,7 +596,7 @@ std::unique_ptr<QMenu> SplitHeader::createChatModeMenu()
this->managedConnections_.managedConnect(
this->modeUpdateRequested_,
[this, setSub, setEmote, setSlow, setR9k, setFollowers]() {
auto twitchChannel =
auto *twitchChannel =
dynamic_cast<TwitchChannel *>(this->split_->getChannel().get());
if (twitchChannel == nullptr)
{
@ -587,7 +607,7 @@ std::unique_ptr<QMenu> SplitHeader::createChatModeMenu()
auto roomModes = twitchChannel->accessRoomModes();
setR9k->setChecked(roomModes->r9k);
setSlow->setChecked(roomModes->slowMode);
setSlow->setChecked(roomModes->slowMode > 0);
setEmote->setChecked(roomModes->emoteOnly);
setSub->setChecked(roomModes->submode);
setFollowers->setChecked(roomModes->followerOnly != -1);
@ -675,7 +695,7 @@ void SplitHeader::updateRoomModes()
void SplitHeader::initializeModeSignals(EffectLabel &label)
{
this->modeUpdateRequested_.connect([this, &label] {
if (auto twitchChannel =
if (auto *twitchChannel =
dynamic_cast<TwitchChannel *>(this->split_->getChannel().get()))
{
label.setEnable(twitchChannel->hasModRights());
@ -710,7 +730,7 @@ void SplitHeader::handleChannelChanged()
this->channelConnections_.clear();
auto channel = this->split_->getChannel();
if (auto twitchChannel = dynamic_cast<TwitchChannel *>(channel.get()))
if (auto *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get()))
{
this->channelConnections_.managedConnect(
twitchChannel->liveStatusChanged, [this]() {
@ -750,9 +770,11 @@ void SplitHeader::updateChannelText()
auto title = channel->getLocalizedName();
if (indirectChannel.getType() == Channel::Type::TwitchWatching)
{
title = "watching: " + (title.isEmpty() ? "none" : title);
}
if (auto twitchChannel = dynamic_cast<TwitchChannel *>(channel.get()))
if (auto *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get()))
{
const auto streamStatus = twitchChannel->accessStreamStatus();
@ -779,7 +801,7 @@ void SplitHeader::updateChannelText()
}
if (!url.isEmpty() &&
(!this->lastThumbnail_.isValid() ||
this->lastThumbnail_.elapsed() > 5 * 60 * 1000))
this->lastThumbnail_.elapsed() > THUMBNAIL_MAX_AGE_MS))
{
NetworkRequest(url, NetworkRequestType::Get)
.onSuccess([this](auto result) -> Outcome {
@ -808,7 +830,7 @@ void SplitHeader::updateChannelText()
}
}
if (!title.isEmpty() && this->split_->getFilters().size() != 0)
if (!title.isEmpty() && !this->split_->getFilters().empty())
{
title += " - filtered";
}
@ -826,7 +848,7 @@ void SplitHeader::updateModerationModeIcon()
: getResources().buttons.modModeDisabled);
auto channel = this->split_->getChannel();
auto twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
auto *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
if (twitchChannel != nullptr &&
(twitchChannel->hasModRights() || moderationMode))
@ -839,7 +861,7 @@ void SplitHeader::updateModerationModeIcon()
}
}
void SplitHeader::paintEvent(QPaintEvent *)
void SplitHeader::paintEvent(QPaintEvent * /*event*/)
{
QPainter painter(this);
@ -891,7 +913,7 @@ void SplitHeader::mousePressEvent(QMouseEvent *event)
this->doubleClicked_ = false;
}
void SplitHeader::mouseReleaseEvent(QMouseEvent *event)
void SplitHeader::mouseReleaseEvent(QMouseEvent * /*event*/)
{
this->dragging_ = false;
}
@ -921,13 +943,13 @@ void SplitHeader::enterEvent(QEvent *event)
{
if (!this->tooltipText_.isEmpty())
{
auto channel = this->split_->getChannel().get();
auto *channel = this->split_->getChannel().get();
if (channel->getType() == Channel::Type::Twitch)
{
dynamic_cast<TwitchChannel *>(channel)->refreshTitle();
}
auto tooltip = TooltipWidget::instance();
auto *tooltip = TooltipWidget::instance();
tooltip->clearImage();
tooltip->setText(this->tooltipText_);
tooltip->setWordWrap(true);
@ -994,7 +1016,7 @@ void SplitHeader::reloadChannelEmotes()
auto channel = this->split_->getChannel();
if (auto twitchChannel = dynamic_cast<TwitchChannel *>(channel.get()))
if (auto *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get()))
{
twitchChannel->refreshFFZChannelEmotes(true);
twitchChannel->refreshBTTVChannelEmotes(true);

View file

@ -25,7 +25,7 @@ class SplitHeader final : public BaseWidget
Q_OBJECT
public:
explicit SplitHeader(Split *_chatWidget);
explicit SplitHeader(Split *split);
void setAddButtonVisible(bool value);
void setViewersButtonVisible(bool value);
@ -35,16 +35,16 @@ public:
void updateRoomModes();
protected:
virtual void scaleChangedEvent(float) override;
virtual void themeChangedEvent() override;
void scaleChangedEvent(float scale) override;
void themeChangedEvent() override;
virtual void paintEvent(QPaintEvent *) override;
virtual void mousePressEvent(QMouseEvent *event) override;
virtual void mouseReleaseEvent(QMouseEvent *event) override;
virtual void mouseMoveEvent(QMouseEvent *event) override;
virtual void enterEvent(QEvent *) override;
virtual void leaveEvent(QEvent *event) override;
virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
void paintEvent(QPaintEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void enterEvent(QEvent *event) override;
void leaveEvent(QEvent *event) override;
void mouseDoubleClickEvent(QMouseEvent *event) override;
private:
void initializeLayout();