mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
feat: add command line argument to select/add tab with a channel (#5111)
This commit is contained in:
parent
acee654bd2
commit
7951af6104
|
@ -24,6 +24,7 @@
|
|||
- Minor: Improved color selection and display. (#5057)
|
||||
- Minor: Improved Streamlink documentation in the settings dialog. (#5076)
|
||||
- Minor: Normalized the input padding between light & dark themes. (#5095)
|
||||
- Minor: Add `--activate <channel>` (or `-a`) command line option to activate or add a Twitch channel. (#5111)
|
||||
- Bugfix: Fixed an issue where certain emojis did not send to Twitch chat correctly. (#4840)
|
||||
- Bugfix: Fixed capitalized channel names in log inclusion list not being logged. (#4848)
|
||||
- Bugfix: Trimmed custom streamlink paths on all platforms making sure you don't accidentally add spaces at the beginning or end of its path. (#4834)
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
namespace {
|
||||
|
||||
using namespace chatterino;
|
||||
|
||||
template <class... Args>
|
||||
QCommandLineOption hiddenOption(Args... args)
|
||||
{
|
||||
|
@ -62,6 +64,29 @@ QStringList extractCommandLine(
|
|||
return args;
|
||||
}
|
||||
|
||||
std::optional<Args::Channel> parseActivateOption(QString input)
|
||||
{
|
||||
auto colon = input.indexOf(u':');
|
||||
if (colon >= 0)
|
||||
{
|
||||
auto ty = input.left(colon);
|
||||
if (ty != u"t")
|
||||
{
|
||||
qCWarning(chatterinoApp).nospace()
|
||||
<< "Failed to parse active channel (unknown type: " << ty
|
||||
<< ")";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
input = input.mid(colon + 1);
|
||||
}
|
||||
|
||||
return Args::Channel{
|
||||
.provider = ProviderId::Twitch,
|
||||
.name = input,
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace chatterino {
|
||||
|
@ -100,6 +125,14 @@ Args::Args(const QApplication &app, const Paths &paths)
|
|||
"If platform isn't specified, default is Twitch.",
|
||||
"t:channel1;t:channel2;...");
|
||||
|
||||
QCommandLineOption activateOption(
|
||||
{"a", "activate"},
|
||||
"Activate the tab with this channel or add one in the main "
|
||||
"window.\nOnly Twitch is "
|
||||
"supported at the moment (prefix: 't:').\nIf the platform isn't "
|
||||
"specified, Twitch is assumed.",
|
||||
"t:channel");
|
||||
|
||||
parser.addOptions({
|
||||
{{"V", "version"}, "Displays version information."},
|
||||
crashRecoveryOption,
|
||||
|
@ -110,6 +143,7 @@ Args::Args(const QApplication &app, const Paths &paths)
|
|||
verboseOption,
|
||||
safeModeOption,
|
||||
channelLayout,
|
||||
activateOption,
|
||||
});
|
||||
|
||||
if (!parser.parse(app.arguments()))
|
||||
|
@ -163,10 +197,17 @@ Args::Args(const QApplication &app, const Paths &paths)
|
|||
this->safeMode = true;
|
||||
}
|
||||
|
||||
if (parser.isSet(activateOption))
|
||||
{
|
||||
this->activateChannel =
|
||||
parseActivateOption(parser.value(activateOption));
|
||||
}
|
||||
|
||||
this->currentArguments_ = extractCommandLine(parser, {
|
||||
verboseOption,
|
||||
safeModeOption,
|
||||
channelLayout,
|
||||
activateOption,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "common/ProviderId.hpp"
|
||||
#include "common/WindowDescriptors.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
|
@ -26,12 +27,18 @@ class Paths;
|
|||
/// -v, --verbose
|
||||
/// -V, --version
|
||||
/// -c, --channels=t:channel1;t:channel2;...
|
||||
/// -a, --activate=t:channel
|
||||
/// --safe-mode
|
||||
///
|
||||
/// See documentation on `QGuiApplication` for documentation on Qt arguments like -platform.
|
||||
class Args
|
||||
{
|
||||
public:
|
||||
struct Channel {
|
||||
ProviderId provider;
|
||||
QString name;
|
||||
};
|
||||
|
||||
Args() = default;
|
||||
Args(const QApplication &app, const Paths &paths);
|
||||
|
||||
|
@ -52,6 +59,7 @@ public:
|
|||
bool dontSaveSettings{};
|
||||
bool dontLoadMainWindow{};
|
||||
std::optional<WindowLayout> customChannelLayout;
|
||||
std::optional<Channel> activateChannel;
|
||||
bool verbose{};
|
||||
bool safeMode{};
|
||||
|
||||
|
|
|
@ -229,4 +229,108 @@ WindowLayout WindowLayout::loadFromFile(const QString &path)
|
|||
return layout;
|
||||
}
|
||||
|
||||
void WindowLayout::activateOrAddChannel(ProviderId provider,
|
||||
const QString &name)
|
||||
{
|
||||
if (provider != ProviderId::Twitch || name.startsWith(u'/') ||
|
||||
name.startsWith(u'$'))
|
||||
{
|
||||
qCWarning(chatterinoWindowmanager)
|
||||
<< "Only twitch channels can be set as active";
|
||||
return;
|
||||
}
|
||||
|
||||
auto mainWindow = std::find_if(this->windows_.begin(), this->windows_.end(),
|
||||
[](const auto &win) {
|
||||
return win.type_ == WindowType::Main;
|
||||
});
|
||||
|
||||
if (mainWindow == this->windows_.end())
|
||||
{
|
||||
this->windows_.emplace_back(WindowDescriptor{
|
||||
.type_ = WindowType::Main,
|
||||
.geometry_ = {-1, -1, -1, -1},
|
||||
.tabs_ =
|
||||
{
|
||||
TabDescriptor{
|
||||
.selected_ = true,
|
||||
.rootNode_ = SplitNodeDescriptor{{
|
||||
.type_ = "twitch",
|
||||
.channelName_ = name,
|
||||
}},
|
||||
},
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
TabDescriptor *bestTab = nullptr;
|
||||
// The tab score is calculated as follows:
|
||||
// +2 for every split
|
||||
// +1 if the desired split has filters
|
||||
// Thus lower is better and having one split of a channel is preferred over multiple
|
||||
size_t bestTabScore = std::numeric_limits<size_t>::max();
|
||||
|
||||
for (auto &tab : mainWindow->tabs_)
|
||||
{
|
||||
tab.selected_ = false;
|
||||
|
||||
if (!tab.rootNode_)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// recursive visitor
|
||||
struct Visitor {
|
||||
const QString &spec;
|
||||
size_t score = 0;
|
||||
bool hasChannel = false;
|
||||
|
||||
void operator()(const SplitNodeDescriptor &split)
|
||||
{
|
||||
this->score += 2;
|
||||
if (split.channelName_ == this->spec)
|
||||
{
|
||||
hasChannel = true;
|
||||
if (!split.filters_.empty())
|
||||
{
|
||||
this->score += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(const ContainerNodeDescriptor &container)
|
||||
{
|
||||
for (const auto &item : container.items_)
|
||||
{
|
||||
std::visit(*this, item);
|
||||
}
|
||||
}
|
||||
} visitor{name};
|
||||
|
||||
std::visit(visitor, *tab.rootNode_);
|
||||
|
||||
if (visitor.hasChannel && visitor.score < bestTabScore)
|
||||
{
|
||||
bestTab = &tab;
|
||||
bestTabScore = visitor.score;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestTab)
|
||||
{
|
||||
bestTab->selected_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
TabDescriptor tab{
|
||||
.selected_ = true,
|
||||
.rootNode_ = SplitNodeDescriptor{{
|
||||
.type_ = "twitch",
|
||||
.channelName_ = name,
|
||||
}},
|
||||
};
|
||||
mainWindow->tabs_.emplace_back(tab);
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "common/ProviderId.hpp"
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QList>
|
||||
#include <QRect>
|
||||
|
@ -95,12 +97,22 @@ struct WindowDescriptor {
|
|||
class WindowLayout
|
||||
{
|
||||
public:
|
||||
static WindowLayout loadFromFile(const QString &path);
|
||||
|
||||
// A complete window layout has a single emote popup position that is shared among all windows
|
||||
QPoint emotePopupPos_;
|
||||
|
||||
std::vector<WindowDescriptor> windows_;
|
||||
|
||||
/// Selects the split containing the channel specified by @a name for the specified
|
||||
/// @a provider. Currently, only Twitch is supported as the provider
|
||||
/// and special channels (such as /mentions) are ignored.
|
||||
///
|
||||
/// Tabs with fewer splits are preferred.
|
||||
/// Channels without filters are preferred.
|
||||
///
|
||||
/// If no split with the channel exists, a new one is added.
|
||||
/// If no window exists, a new one is added.
|
||||
void activateOrAddChannel(ProviderId provider, const QString &name);
|
||||
static WindowLayout loadFromFile(const QString &path);
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -365,6 +365,12 @@ void WindowManager::initialize(Settings &settings, const Paths &paths)
|
|||
windowLayout = this->loadWindowLayoutFromFile();
|
||||
}
|
||||
|
||||
auto desired = getIApp()->getArgs().activateChannel;
|
||||
if (desired)
|
||||
{
|
||||
windowLayout.activateOrAddChannel(desired->provider, desired->name);
|
||||
}
|
||||
|
||||
this->emotePopupPos_ = windowLayout.emotePopupPos_;
|
||||
|
||||
this->applyWindowLayout(windowLayout);
|
||||
|
|
Loading…
Reference in a new issue