diff --git a/CHANGELOG.md b/CHANGELOG.md index 09d2df90e..5cf243e57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Minor: Channel name in ` has gone offline. Exiting host mode.` messages is now clickable. (#2922) - Minor: Added `/openurl` command. Usage: `/openurl `. Opens the provided URL in the browser. (#2461, #2926) - Minor: Updated to Emoji v13.1 (#2958) +- Minor: Added "Open in: new tab, browser player, streamlink" in twitch link context menu. (#2988) - Minor: Sender username in automod messages shown to moderators shows correct color and display name. (#2967) - Bugfix: Now deleting cache files that weren't modified in the past 14 days. (#2947) - Bugfix: Fixed large timeout durations in moderation buttons overlapping with usernames or other buttons. (#2865, #2921) diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index 8986fede6..37945a86e 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -1924,7 +1924,7 @@ void ChannelView::addContextMenuItems( crossPlatformCopy(copyString); }); - // Open in new split. + // If is a link to a twitch user/stream if (hoveredElement->getLink().type == Link::Url) { static QRegularExpression twitchChannelRegex( @@ -1943,7 +1943,22 @@ void ChannelView::addContextMenuItems( { menu->addSeparator(); menu->addAction("Open in new split", [twitchUsername, this] { - this->joinToChannel.invoke(twitchUsername); + this->openChannelIn.invoke(twitchUsername, + FromTwitchLinkOpenChannelIn::Split); + }); + menu->addAction("Open in new tab", [twitchUsername, this] { + this->openChannelIn.invoke(twitchUsername, + FromTwitchLinkOpenChannelIn::Tab); + }); + + 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); }); } } diff --git a/src/widgets/helper/ChannelView.hpp b/src/widgets/helper/ChannelView.hpp index 5b7661678..13f672157 100644 --- a/src/widgets/helper/ChannelView.hpp +++ b/src/widgets/helper/ChannelView.hpp @@ -47,6 +47,13 @@ enum class PauseReason { KeyboardModifier, }; +enum class FromTwitchLinkOpenChannelIn { + Split, + Tab, + BrowserPlayer, + Streamlink, +}; + using SteadyClock = std::chrono::steady_clock; class ChannelView final : public BaseWidget @@ -96,7 +103,8 @@ public: pajlada::Signals::Signal tabHighlightRequested; pajlada::Signals::NoArgSignal liveStatusChanged; pajlada::Signals::Signal linkClicked; - pajlada::Signals::Signal joinToChannel; + pajlada::Signals::Signal + openChannelIn; protected: void themeChangedEvent() override; diff --git a/src/widgets/splits/Split.cpp b/src/widgets/splits/Split.cpp index be3e50763..02af88573 100644 --- a/src/widgets/splits/Split.cpp +++ b/src/widgets/splits/Split.cpp @@ -163,9 +163,30 @@ Split::Split(QWidget *parent) } }); - this->view_->joinToChannel.connect([this](QString twitchChannel) { - this->openSplitRequested.invoke( - getApp()->twitch.server->getOrAddChannel(twitchChannel)); + this->view_->openChannelIn.connect([this]( + QString twitchChannel, + FromTwitchLinkOpenChannelIn openIn) { + ChannelPtr channel = + getApp()->twitch.server->getOrAddChannel(twitchChannel); + switch (openIn) + { + case FromTwitchLinkOpenChannelIn::Split: + this->openSplitRequested.invoke(channel); + break; + case FromTwitchLinkOpenChannelIn::Tab: + this->joinChannelInNewTab(channel); + break; + case FromTwitchLinkOpenChannelIn::BrowserPlayer: + this->openChannelInBrowserPlayer(channel); + break; + case FromTwitchLinkOpenChannelIn::Streamlink: + this->openChannelInStreamlink(twitchChannel); + break; + default: + qCWarning(chatterinoWidget) + << "Unhandled \"FromTwitchLinkOpenChannelIn\" enum value: " + << static_cast(openIn); + } }); this->input_->textChanged.connect([=](const QString &newText) { @@ -309,6 +330,39 @@ void Split::updateInputPlaceholder() this->input_->ui_.textEdit->setPlaceholderText(placeholderText); } +void Split::joinChannelInNewTab(ChannelPtr channel) +{ + auto &nb = getApp()->windows->getMainWindow().getNotebook(); + SplitContainer *container = nb.addPage(true); + + Split *split = new Split(container); + split->setChannel(channel); + container->appendSplit(split); +} + +void Split::openChannelInBrowserPlayer(ChannelPtr channel) +{ + if (auto twitchChannel = dynamic_cast(channel.get())) + { + QDesktopServices::openUrl( + "https://player.twitch.tv/?parent=twitch.tv&channel=" + + twitchChannel->getName()); + } +} + +void Split::openChannelInStreamlink(QString channelName) +{ + try + { + openStreamlinkForChannel(channelName); + } + catch (const Exception &ex) + { + qCWarning(chatterinoWidget) + << "Error in doOpenStreamlink:" << ex.what(); + } +} + IndirectChannel Split::getIndirectChannel() { return this->channel_; @@ -595,13 +649,7 @@ void Split::openWhispersInBrowser() void Split::openBrowserPlayer() { - ChannelPtr channel = this->getChannel(); - if (auto twitchChannel = dynamic_cast(channel.get())) - { - QDesktopServices::openUrl( - "https://player.twitch.tv/?parent=twitch.tv&channel=" + - twitchChannel->getName()); - } + this->openChannelInBrowserPlayer(this->getChannel()); } void Split::openModViewInBrowser() @@ -617,15 +665,7 @@ void Split::openModViewInBrowser() void Split::openInStreamlink() { - try - { - openStreamlinkForChannel(this->getChannel()->getName()); - } - catch (const Exception &ex) - { - qCWarning(chatterinoWidget) - << "Error in doOpenStreamlink:" << ex.what(); - } + this->openChannelInStreamlink(this->getChannel()->getName()); } void Split::openWithCustomScheme() diff --git a/src/widgets/splits/Split.hpp b/src/widgets/splits/Split.hpp index 8ba4b5b7c..193f2f796 100644 --- a/src/widgets/splits/Split.hpp +++ b/src/widgets/splits/Split.hpp @@ -114,6 +114,19 @@ private: void handleModifiers(Qt::KeyboardModifiers modifiers); void updateInputPlaceholder(); + /** + * @brief Opens twitch channel stream in a browser player (opens a formatted link) + */ + void openChannelInBrowserPlayer(ChannelPtr channel); + /** + * @brief Opens twitch channel stream in streamlink app (if stream is live and streamlink is installed) + */ + void openChannelInStreamlink(QString channelName); + /** + * @brief Opens twitch channel chat in a new chatterino tab + */ + void joinChannelInNewTab(ChannelPtr channel); + IndirectChannel channel_; bool moderationMode_{};