mirror-chatterino2/src/widgets/splits/SplitHeader.cpp

514 lines
16 KiB
C++
Raw Normal View History

#include "widgets/splits/SplitHeader.hpp"
2018-06-26 14:09:39 +02:00
#include "Application.hpp"
2018-06-26 17:20:03 +02:00
#include "common/UrlFetch.hpp"
#include "controllers/accounts/AccountController.hpp"
2018-06-26 14:09:39 +02:00
#include "providers/twitch/TwitchChannel.hpp"
#include "providers/twitch/TwitchServer.hpp"
2018-06-28 19:46:45 +02:00
#include "singletons/Resources.hpp"
2018-06-28 20:03:04 +02:00
#include "singletons/Theme.hpp"
2018-06-26 14:09:39 +02:00
#include "util/LayoutCreator.hpp"
#include "widgets/Label.hpp"
2018-06-26 17:20:03 +02:00
#include "widgets/TooltipWidget.hpp"
#include "widgets/splits/Split.hpp"
#include "widgets/splits/SplitContainer.hpp"
2017-01-01 02:30:42 +01:00
2017-01-15 16:38:30 +01:00
#include <QByteArray>
#include <QDrag>
#include <QInputDialog>
2017-01-15 16:38:30 +01:00
#include <QMimeData>
#include <QPainter>
2018-01-19 14:48:17 +01:00
#ifdef USEWEBENGINE
2018-06-26 14:09:39 +02:00
#include "widgets/StreamView.hpp"
2018-01-19 14:48:17 +01:00
#endif
2017-04-14 17:52:22 +02:00
namespace chatterino {
2017-01-18 21:30:23 +01:00
2018-01-13 04:05:38 +01:00
SplitHeader::SplitHeader(Split *_split)
: BaseWidget(_split)
2018-07-06 19:23:47 +02:00
, split_(_split)
2017-01-01 02:30:42 +01:00
{
2018-07-06 19:23:47 +02:00
this->split_->focused.connect([this]() { this->themeChangedEvent(); });
this->split_->focusLost.connect([this]() { this->themeChangedEvent(); });
auto app = getApp();
2018-06-26 17:06:17 +02:00
LayoutCreator<SplitHeader> layoutCreator(this);
2018-01-13 04:05:38 +01:00
auto layout = layoutCreator.emplace<QHBoxLayout>().withoutMargin();
2018-07-04 13:05:54 +02:00
layout->setSpacing(0);
2018-01-13 04:05:38 +01:00
{
// channel name label
2018-06-11 15:04:54 +02:00
auto title = layout.emplace<Label>().assign(&this->titleLabel);
title->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
2018-06-11 15:04:54 +02:00
title->setCentered(true);
title->setHasOffset(false);
2018-01-14 22:24:21 +01:00
2018-05-24 08:58:34 +02:00
// mode button
2018-07-06 19:23:47 +02:00
auto mode = layout.emplace<RippleEffectLabel>(nullptr).assign(&this->modeButton_);
mode->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
2018-05-24 10:07:31 +02:00
mode->hide();
2018-05-24 08:58:34 +02:00
this->setupModeLabel(*mode);
QObject::connect(mode.getElement(), &RippleEffectLabel::clicked, this, [this] {
QTimer::singleShot(80, this, [&, this] {
2018-07-06 19:23:47 +02:00
ChannelPtr _channel = this->split_->getChannel();
if (_channel->hasModRights()) {
this->modeMenu_.popup(QCursor::pos());
}
});
});
2018-05-24 08:58:34 +02:00
2018-01-13 04:05:38 +01:00
// moderation mode
2018-07-06 19:23:47 +02:00
auto moderator = layout.emplace<RippleEffectButton>(this).assign(&this->moderationButton_);
2018-01-17 16:52:51 +01:00
2018-07-04 13:05:54 +02:00
QObject::connect(moderator.getElement(), &RippleEffectButton::clicked, this,
[this, moderator]() mutable {
2018-07-06 19:23:47 +02:00
this->split_->setModerationMode(!this->split_->getModerationMode());
2018-07-04 13:05:54 +02:00
2018-07-06 19:23:47 +02:00
moderator->setDim(!this->split_->getModerationMode());
2018-07-04 13:05:54 +02:00
});
2018-01-17 16:52:51 +01:00
this->updateModerationModeIcon();
2018-07-04 13:05:54 +02:00
// dropdown label
2018-07-06 19:23:47 +02:00
auto dropdown = layout.emplace<RippleEffectButton>(this).assign(&this->dropdownButton_);
2018-07-04 13:05:54 +02:00
dropdown->setMouseTracking(true);
// dropdown->setPixmap(*app->resources->splitHeaderContext->getPixmap());
// dropdown->setScaleIndependantSize(23, 23);
this->addDropdownItems(dropdown.getElement());
QObject::connect(dropdown.getElement(), &RippleEffectButton::leftMousePress, this, [this] {
2018-07-04 13:05:54 +02:00
QTimer::singleShot(80, [&, this] {
this->dropdownMenu_.popup(QCursor::pos());
2018-07-04 13:05:54 +02:00
});
});
2018-01-13 04:05:38 +01:00
}
// ---- misc
this->layout()->setMargin(0);
2018-01-25 21:11:14 +01:00
this->scaleChangedEvent(this->getScale());
2017-06-11 09:11:55 +02:00
this->updateChannelText();
2017-01-15 16:38:30 +01:00
this->initializeChannelSignals();
2018-07-06 19:23:47 +02:00
this->split_->channelChanged.connect([this]() {
this->initializeChannelSignals(); //
});
2018-05-26 20:26:25 +02:00
this->managedConnect(app->accounts->twitch.currentUserChanged,
[this] { this->updateModerationModeIcon(); });
2018-07-06 19:23:47 +02:00
this->addModeActions(this->modeMenu_);
this->setMouseTracking(true);
}
2018-01-13 04:05:38 +01:00
SplitHeader::~SplitHeader()
{
2018-07-06 19:23:47 +02:00
this->onlineStatusChangedConnection_.disconnect();
2018-01-13 04:05:38 +01:00
}
void SplitHeader::addDropdownItems(RippleEffectButton *)
2018-01-13 04:05:38 +01:00
{
// clang-format off
2018-07-06 19:23:47 +02:00
this->dropdownMenu_.addAction("New split", this->split_, &Split::doAddSplit, QKeySequence(tr("Ctrl+T")));
this->dropdownMenu_.addAction("Close split", this->split_, &Split::doCloseSplit, QKeySequence(tr("Ctrl+W")));
this->dropdownMenu_.addAction("Change channel", this->split_, &Split::doChangeChannel, QKeySequence(tr("Ctrl+R")));
this->dropdownMenu_.addSeparator();
this->dropdownMenu_.addAction("Viewer list", this->split_, &Split::doOpenViewerList);
this->dropdownMenu_.addAction("Search", this->split_, &Split::doSearch, QKeySequence(tr("Ctrl+F")));
this->dropdownMenu_.addSeparator();
this->dropdownMenu_.addAction("Popup", this->split_, &Split::doPopup);
2018-01-19 14:48:17 +01:00
#ifdef USEWEBENGINE
this->dropdownMenu.addAction("Start watching", this, [this]{
ChannelPtr _channel = this->split->getChannel();
2018-02-05 15:11:50 +01:00
TwitchChannel *tc = dynamic_cast<TwitchChannel *>(_channel.get());
2018-01-19 14:48:17 +01:00
if (tc != nullptr) {
StreamView *view = new StreamView(_channel, "https://player.twitch.tv/?channel=" + tc->name);
view->setAttribute(Qt::WA_DeleteOnClose, true);
view->show();
}
});
#endif
2018-07-06 19:23:47 +02:00
this->dropdownMenu_.addAction("Open browser", this->split_, &Split::doOpenChannel);
2018-01-19 14:48:17 +01:00
#ifndef USEWEBENGINE
2018-07-06 19:23:47 +02:00
this->dropdownMenu_.addAction("Open browser popup", this->split_, &Split::doOpenPopupPlayer);
2018-01-19 14:48:17 +01:00
#endif
2018-07-06 19:23:47 +02:00
this->dropdownMenu_.addAction("Open streamlink", this->split_, &Split::doOpenStreamlink);
this->dropdownMenu_.addSeparator();
this->dropdownMenu_.addAction("Reload channel emotes", this, SLOT(menuReloadChannelEmotes()));
this->dropdownMenu_.addAction("Reconnect", this, SLOT(menuManualReconnect()));
this->dropdownMenu_.addAction("Clear messages", this->split_, &Split::doClearChat);
// this->dropdownMenu.addSeparator();
// this->dropdownMenu.addAction("Show changelog", this, SLOT(menuShowChangelog()));
2018-01-13 04:05:38 +01:00
// clang-format on
}
void SplitHeader::updateRoomModes()
{
2018-07-04 13:05:54 +02:00
this->modeUpdateRequested_.invoke();
}
void SplitHeader::setupModeLabel(RippleEffectLabel &label)
{
2018-07-06 19:23:47 +02:00
this->managedConnections_.push_back(this->modeUpdateRequested_.connect([this, &label] {
auto twitchChannel = dynamic_cast<TwitchChannel *>(this->split_->getChannel().get());
// return if the channel is not a twitch channel
if (twitchChannel == nullptr) {
label.hide();
return;
}
// set lable enabled
label.setEnable(twitchChannel->hasModRights());
// set the label text
auto roomModes = twitchChannel->getRoomModes();
QString text;
if (roomModes.r9k)
text += "r9k, ";
if (roomModes.slowMode)
text += QString("slow(%1), ").arg(QString::number(roomModes.slowMode));
if (roomModes.emoteOnly)
text += "emote, ";
if (roomModes.submode)
text += "sub, ";
if (text.length() > 2) {
text = text.mid(0, text.size() - 2);
}
if (text.isEmpty()) {
if (twitchChannel->hasModRights()) {
label.getLabel().setText("none");
label.show();
} else {
label.hide();
}
} else {
static QRegularExpression commaReplacement("^.+?, .+?,( ).+$");
QRegularExpressionMatch match = commaReplacement.match(text);
if (match.hasMatch()) {
text = text.mid(0, match.capturedStart(1)) + '\n' + text.mid(match.capturedEnd(1));
}
label.getLabel().setText(text);
label.show();
}
}));
}
2018-07-04 13:05:54 +02:00
void SplitHeader::addModeActions(QMenu &menu)
{
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);
setSub->setCheckable(true);
setEmote->setCheckable(true);
setSlow->setCheckable(true);
setR9k->setCheckable(true);
2018-07-04 13:05:54 +02:00
menu.addAction(setEmote);
menu.addAction(setSub);
menu.addAction(setSlow);
menu.addAction(setR9k);
2018-07-06 19:23:47 +02:00
this->managedConnections_.push_back(this->modeUpdateRequested_.connect( //
2018-07-04 13:05:54 +02:00
[this, setSub, setEmote, setSlow, setR9k]() {
2018-07-06 19:23:47 +02:00
auto twitchChannel = dynamic_cast<TwitchChannel *>(this->split_->getChannel().get());
2018-07-04 13:05:54 +02:00
if (twitchChannel == nullptr) {
2018-07-06 19:23:47 +02:00
this->modeButton_->hide();
2018-07-04 13:05:54 +02:00
return;
}
auto roomModes = twitchChannel->getRoomModes();
setR9k->setChecked(roomModes.r9k);
setSlow->setChecked(roomModes.slowMode);
setEmote->setChecked(roomModes.emoteOnly);
setSub->setChecked(roomModes.submode);
}));
2018-07-04 13:05:54 +02:00
auto toggle = [this](const QString &_command, QAction *action) mutable {
QString command = _command;
2018-07-04 13:05:54 +02:00
if (!action->isChecked()) {
command += "off";
};
action->setChecked(!action->isChecked());
2018-07-04 13:05:54 +02:00
qDebug() << command;
2018-07-06 19:23:47 +02:00
this->split_->getChannel().get()->sendMessage(command);
};
QObject::connect(setSub, &QAction::triggered, this,
[setSub, toggle]() mutable { toggle("/subscribers", setSub); });
QObject::connect(setEmote, &QAction::triggered, this,
[setEmote, toggle]() mutable { toggle("/emoteonly", setEmote); });
QObject::connect(setSlow, &QAction::triggered, this, [setSlow, this]() {
if (!setSlow->isChecked()) {
2018-07-06 19:23:47 +02:00
this->split_->getChannel().get()->sendMessage("/slowoff");
setSlow->setChecked(false);
return;
};
bool ok;
int slowSec =
QInputDialog::getInt(this, "", "Seconds:", 10, 0, 500, 1, &ok, Qt::FramelessWindowHint);
if (ok) {
2018-07-06 19:23:47 +02:00
this->split_->getChannel().get()->sendMessage(QString("/slow %1").arg(slowSec));
} else {
setSlow->setChecked(false);
}
});
QObject::connect(setR9k, &QAction::triggered, this,
[setR9k, toggle]() mutable { toggle("/r9kbeta", setR9k); });
}
2017-11-12 17:21:50 +01:00
void SplitHeader::initializeChannelSignals()
{
// Disconnect any previous signal first
2018-07-06 19:23:47 +02:00
this->onlineStatusChangedConnection_.disconnect();
2018-07-06 19:23:47 +02:00
auto channel = this->split_->getChannel();
2018-02-05 15:11:50 +01:00
TwitchChannel *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
if (twitchChannel) {
2018-07-06 19:23:47 +02:00
this->managedConnections_.emplace_back(twitchChannel->updateLiveInfo.connect([this]() {
this->updateChannelText(); //
}));
}
2017-01-15 16:38:30 +01:00
}
2018-01-25 21:11:14 +01:00
void SplitHeader::scaleChangedEvent(float scale)
2017-09-22 00:50:43 +02:00
{
int w = int(28 * scale);
2018-01-13 04:05:38 +01:00
this->setFixedHeight(w);
2018-07-06 19:23:47 +02:00
this->dropdownButton_->setFixedWidth(w);
this->moderationButton_->setFixedWidth(w);
2018-01-27 21:13:22 +01:00
// this->titleLabel->setFont(
// FontManager::getInstance().getFont(FontStyle::Medium, scale));
2017-09-22 00:50:43 +02:00
}
2017-11-12 17:21:50 +01:00
void SplitHeader::updateChannelText()
2017-01-17 00:15:44 +01:00
{
2018-07-06 19:23:47 +02:00
auto indirectChannel = this->split_->getIndirectChannel();
auto channel = this->split_->getChannel();
2018-04-20 19:54:45 +02:00
QString title = channel->name;
2018-07-06 17:30:12 +02:00
if (indirectChannel.getType() == Channel::Type::TwitchWatching) {
title = "watching: " + (title.isEmpty() ? "none" : title);
}
TwitchChannel *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
2017-09-22 00:50:43 +02:00
if (twitchChannel != nullptr) {
const auto streamStatus = twitchChannel->getStreamStatus();
if (streamStatus.live) {
2018-07-06 19:23:47 +02:00
this->isLive_ = true;
this->tooltip_ = "<style>.center { text-align: center; }</style>"
"<p class = \"center\">" +
streamStatus.title + "<br><br>" + streamStatus.game + "<br>" +
(streamStatus.rerun ? "Vod-casting" : "Live") + " for " +
streamStatus.uptime + " with " +
QString::number(streamStatus.viewerCount) +
" viewers"
"</p>";
if (streamStatus.rerun) {
title += " (rerun)";
} else if (streamStatus.streamType.isEmpty()) {
title += " (" + streamStatus.streamType + ")";
} else {
title += " (live)";
}
} else {
2018-07-06 19:23:47 +02:00
this->tooltip_ = QString();
}
}
if (title.isEmpty()) {
title = "<empty>";
}
2018-07-06 19:23:47 +02:00
this->isLive_ = false;
this->titleLabel->setText(title);
2017-01-17 00:15:44 +01:00
}
2018-01-17 16:52:51 +01:00
void SplitHeader::updateModerationModeIcon()
{
auto app = getApp();
2018-07-06 19:23:47 +02:00
this->moderationButton_->setPixmap(this->split_->getModerationMode()
? *app->resources->moderationmode_enabled->getPixmap()
: *app->resources->moderationmode_disabled->getPixmap());
2018-01-17 17:17:26 +01:00
2018-01-17 18:36:12 +01:00
bool modButtonVisible = false;
2018-07-06 19:23:47 +02:00
ChannelPtr channel = this->split_->getChannel();
2018-01-17 18:36:12 +01:00
2018-02-05 15:11:50 +01:00
TwitchChannel *tc = dynamic_cast<TwitchChannel *>(channel.get());
2018-01-17 18:36:12 +01:00
if (tc != nullptr && tc->hasModRights()) {
modButtonVisible = true;
}
2018-07-06 19:23:47 +02:00
this->moderationButton_->setVisible(modButtonVisible);
2018-01-17 16:52:51 +01:00
}
2017-11-12 17:21:50 +01:00
void SplitHeader::paintEvent(QPaintEvent *)
2017-01-01 02:30:42 +01:00
{
QPainter painter(this);
2018-07-06 17:11:37 +02:00
painter.fillRect(rect(), this->theme->splits.header.background);
painter.setPen(this->theme->splits.header.border);
2017-01-01 02:30:42 +01:00
painter.drawRect(0, 0, width() - 1, height() - 1);
}
2017-11-12 17:21:50 +01:00
void SplitHeader::mousePressEvent(QMouseEvent *event)
2017-01-01 02:30:42 +01:00
{
2018-05-16 14:55:45 +02:00
if (event->button() == Qt::LeftButton) {
2018-07-06 19:23:47 +02:00
this->dragging_ = true;
2018-05-16 14:55:45 +02:00
2018-07-06 19:23:47 +02:00
this->dragStart_ = event->pos();
2018-05-16 14:55:45 +02:00
}
2017-01-01 02:30:42 +01:00
2018-07-06 19:23:47 +02:00
this->doubleClicked_ = false;
2018-05-16 14:55:45 +02:00
}
void SplitHeader::mouseReleaseEvent(QMouseEvent *event)
{
2018-07-06 19:23:47 +02:00
if (this->dragging_ && event->button() == Qt::LeftButton) {
2018-05-16 14:55:45 +02:00
QPoint pos = event->globalPos();
2018-07-06 19:23:47 +02:00
if (!showingHelpTooltip_) {
this->showingHelpTooltip_ = true;
2018-05-16 14:55:45 +02:00
QTimer::singleShot(400, this, [this, pos] {
2018-07-06 19:23:47 +02:00
if (this->doubleClicked_) {
this->doubleClicked_ = false;
this->showingHelpTooltip_ = false;
2018-05-16 14:55:45 +02:00
return;
}
TooltipWidget *widget = new TooltipWidget();
widget->setText("Double click or press <Ctrl+R> to change the channel.\nClick and "
2018-05-16 14:55:45 +02:00
"drag to move the split.");
widget->setAttribute(Qt::WA_DeleteOnClose);
widget->move(pos);
widget->show();
2018-05-24 10:07:31 +02:00
widget->raise();
2018-05-16 14:55:45 +02:00
QTimer::singleShot(3000, widget, [this, widget] {
widget->close();
2018-07-06 19:23:47 +02:00
this->showingHelpTooltip_ = false;
2018-05-16 14:55:45 +02:00
});
});
}
}
2018-07-06 19:23:47 +02:00
this->dragging_ = false;
2017-01-01 02:30:42 +01:00
}
2017-11-12 17:21:50 +01:00
void SplitHeader::mouseMoveEvent(QMouseEvent *event)
2017-01-01 02:30:42 +01:00
{
2018-07-06 19:23:47 +02:00
if (this->dragging_) {
if (std::abs(this->dragStart_.x() - event->pos().x()) > int(12 * this->getScale()) ||
std::abs(this->dragStart_.y() - event->pos().y()) > int(12 * this->getScale())) {
this->split_->drag();
this->dragging_ = false;
2018-05-11 13:55:10 +02:00
}
}
2017-01-01 02:30:42 +01:00
}
2017-11-12 17:21:50 +01:00
void SplitHeader::mouseDoubleClickEvent(QMouseEvent *event)
2017-01-17 00:15:44 +01:00
{
if (event->button() == Qt::LeftButton) {
2018-07-06 19:23:47 +02:00
this->split_->doChangeChannel();
2017-01-17 00:15:44 +01:00
}
2018-07-06 19:23:47 +02:00
this->doubleClicked_ = true;
2017-01-17 00:15:44 +01:00
}
void SplitHeader::enterEvent(QEvent *event)
{
2018-07-06 19:23:47 +02:00
if (!this->tooltip_.isEmpty()) {
auto tooltipWidget = TooltipWidget::getInstance();
2018-05-26 17:11:09 +02:00
tooltipWidget->moveTo(this, this->mapToGlobal(this->rect().bottomLeft()), false);
2018-07-06 19:23:47 +02:00
tooltipWidget->setText(this->tooltip_);
tooltipWidget->show();
tooltipWidget->raise();
}
BaseWidget::enterEvent(event);
}
2018-05-16 14:55:45 +02:00
void SplitHeader::leaveEvent(QEvent *event)
{
TooltipWidget::getInstance()->hide();
2018-05-16 14:55:45 +02:00
BaseWidget::leaveEvent(event);
}
2017-11-12 17:21:50 +01:00
void SplitHeader::rightButtonClicked()
2017-01-15 16:38:30 +01:00
{
}
2018-07-06 17:11:37 +02:00
void SplitHeader::themeChangedEvent()
{
QPalette palette;
2018-07-06 19:23:47 +02:00
if (this->split_->hasFocus()) {
2018-07-06 17:11:37 +02:00
palette.setColor(QPalette::Foreground, this->theme->splits.header.focusedText);
} else {
2018-07-06 17:11:37 +02:00
palette.setColor(QPalette::Foreground, this->theme->splits.header.text);
}
2018-07-06 17:11:37 +02:00
if (this->theme->isLightTheme()) {
2018-07-06 19:23:47 +02:00
this->dropdownButton_->setPixmap(QPixmap(":/images/menu_black.png"));
2018-07-04 14:13:29 +02:00
} else {
2018-07-06 19:23:47 +02:00
this->dropdownButton_->setPixmap(QPixmap(":/images/menu_white.png"));
2018-07-04 14:13:29 +02:00
}
2018-01-13 04:05:38 +01:00
this->titleLabel->setPalette(palette);
}
2017-11-12 17:21:50 +01:00
void SplitHeader::menuMoveSplit()
2017-01-15 16:38:30 +01:00
{
}
2017-11-12 17:21:50 +01:00
void SplitHeader::menuReloadChannelEmotes()
2017-01-15 16:38:30 +01:00
{
2018-07-06 19:23:47 +02:00
auto channel = this->split_->getChannel();
TwitchChannel *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
if (twitchChannel) {
twitchChannel->reloadChannelEmotes();
}
2017-01-15 16:38:30 +01:00
}
2017-11-12 17:21:50 +01:00
void SplitHeader::menuManualReconnect()
2017-01-15 16:38:30 +01:00
{
auto app = getApp();
2018-02-05 15:11:50 +01:00
// fourtf: connection
app->twitch.server->connect();
2017-01-15 16:38:30 +01:00
}
2017-11-12 17:21:50 +01:00
void SplitHeader::menuShowChangelog()
2017-01-15 16:38:30 +01:00
{
}
} // namespace chatterino