Added x-attach-split-to-window command line arg (#2411)

Co-authored-by: pajlada <rasmus.karlsson@pajlada.com>
This commit is contained in:
fourtf 2021-04-17 16:15:23 +02:00 committed by GitHub
parent 58017a7546
commit 2db140d5af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 350 additions and 160 deletions

View file

@ -61,6 +61,7 @@
- Minor: Made username autocompletion truecase (#1199, #1883) - Minor: Made username autocompletion truecase (#1199, #1883)
- Minor: Update the listing of top-level domains. (#2345) - Minor: Update the listing of top-level domains. (#2345)
- Minor: Properly respect RECONNECT messages from Twitch (#2347) - Minor: Properly respect RECONNECT messages from Twitch (#2347)
- Minor: Added command line option to attach chatterino to another window.
- Minor: Hide "Case-sensitive" column for user highlights. (#2404) - Minor: Hide "Case-sensitive" column for user highlights. (#2404)
- Minor: Added human-readable formatting to remaining timeout duration. (#2398) - Minor: Added human-readable formatting to remaining timeout duration. (#2398)
- Minor: Update emojis version to 13 (2020). (#1555) - Minor: Update emojis version to 13 (2020). (#1555)

View file

@ -259,6 +259,7 @@ SOURCES += \
src/widgets/BasePopup.cpp \ src/widgets/BasePopup.cpp \
src/widgets/BaseWidget.cpp \ src/widgets/BaseWidget.cpp \
src/widgets/BaseWindow.cpp \ src/widgets/BaseWindow.cpp \
src/widgets/FramelessEmbedWindow.cpp \
src/widgets/dialogs/ChannelFilterEditorDialog.cpp \ src/widgets/dialogs/ChannelFilterEditorDialog.cpp \
src/widgets/dialogs/ColorPickerDialog.cpp \ src/widgets/dialogs/ColorPickerDialog.cpp \
src/widgets/dialogs/EmotePopup.cpp \ src/widgets/dialogs/EmotePopup.cpp \
@ -512,6 +513,7 @@ HEADERS += \
src/widgets/BasePopup.hpp \ src/widgets/BasePopup.hpp \
src/widgets/BaseWidget.hpp \ src/widgets/BaseWidget.hpp \
src/widgets/BaseWindow.hpp \ src/widgets/BaseWindow.hpp \
src/widgets/FramelessEmbedWindow.hpp \
src/widgets/dialogs/ChannelFilterEditorDialog.hpp \ src/widgets/dialogs/ChannelFilterEditorDialog.hpp \
src/widgets/dialogs/ColorPickerDialog.hpp \ src/widgets/dialogs/ColorPickerDialog.hpp \
src/widgets/dialogs/EmotePopup.hpp \ src/widgets/dialogs/EmotePopup.hpp \

View file

@ -76,7 +76,8 @@ void Application::initialize(Settings &settings, Paths &paths)
isAppInitialized = true; isAppInitialized = true;
// Show changelog // Show changelog
if (getSettings()->currentVersion.getValue() != "" && if (!getArgs().isFramelessEmbed &&
getSettings()->currentVersion.getValue() != "" &&
getSettings()->currentVersion.getValue() != CHATTERINO_VERSION) getSettings()->currentVersion.getValue() != CHATTERINO_VERSION)
{ {
auto box = new QMessageBox(QMessageBox::Information, "Chatterino 2", auto box = new QMessageBox(QMessageBox::Information, "Chatterino 2",
@ -90,11 +91,14 @@ void Application::initialize(Settings &settings, Paths &paths)
} }
} }
getSettings()->currentVersion.setValue(CHATTERINO_VERSION); if (!getArgs().isFramelessEmbed)
if (getSettings()->enableExperimentalIrc)
{ {
Irc::instance().load(); getSettings()->currentVersion.setValue(CHATTERINO_VERSION);
if (getSettings()->enableExperimentalIrc)
{
Irc::instance().load();
}
} }
for (auto &singleton : this->singletons_) for (auto &singleton : this->singletons_)
@ -103,7 +107,7 @@ void Application::initialize(Settings &settings, Paths &paths)
} }
// add crash message // add crash message
if (getArgs().crashRecovery) if (!getArgs().isFramelessEmbed && getArgs().crashRecovery)
{ {
if (auto selected = if (auto selected =
this->windows->getMainWindow().getNotebook().getSelectedPage()) this->windows->getMainWindow().getNotebook().getSelectedPage())
@ -126,7 +130,10 @@ void Application::initialize(Settings &settings, Paths &paths)
this->windows->updateWordTypeMask(); this->windows->updateWordTypeMask();
this->initNm(paths); if (!getArgs().isFramelessEmbed)
{
this->initNm(paths);
}
this->initPubsub(); this->initPubsub();
} }
@ -136,7 +143,10 @@ int Application::run(QApplication &qtApp)
this->twitch.server->connect(); this->twitch.server->connect();
this->windows->getMainWindow().show(); if (!getArgs().isFramelessEmbed)
{
this->windows->getMainWindow().show();
}
getSettings()->betaUpdates.connect( getSettings()->betaUpdates.connect(
[] { [] {

View file

@ -302,6 +302,8 @@ set(SOURCE_FILES main.cpp
widgets/BaseWidget.hpp widgets/BaseWidget.hpp
widgets/BaseWindow.cpp widgets/BaseWindow.cpp
widgets/BaseWindow.hpp widgets/BaseWindow.hpp
widgets/FramelessEmbedWindow.cpp
widgets/FramelessEmbedWindow.hpp
widgets/Label.cpp widgets/Label.cpp
widgets/Label.hpp widgets/Label.hpp
widgets/Notebook.cpp widgets/Notebook.cpp

View file

@ -26,6 +26,9 @@ Args::Args(const QApplication &app)
// Added to ignore the parent-window option passed during native messaging // Added to ignore the parent-window option passed during native messaging
QCommandLineOption parentWindowOption("parent-window"); QCommandLineOption parentWindowOption("parent-window");
parentWindowOption.setFlags(QCommandLineOption::HiddenFromHelp); parentWindowOption.setFlags(QCommandLineOption::HiddenFromHelp);
QCommandLineOption parentWindowIdOption("x-attach-split-to-window", "",
"window-id");
parentWindowIdOption.setFlags(QCommandLineOption::HiddenFromHelp);
// Verbose // Verbose
QCommandLineOption verboseOption({{"v", "verbose"}, QCommandLineOption verboseOption({{"v", "verbose"},
@ -37,6 +40,7 @@ Args::Args(const QApplication &app)
{{"V", "version"}, "Displays version information."}, {{"V", "version"}, "Displays version information."},
crashRecoveryOption, crashRecoveryOption,
parentWindowOption, parentWindowOption,
parentWindowIdOption,
verboseOption, verboseOption,
}); });
parser.addOption(QCommandLineOption( parser.addOption(QCommandLineOption(
@ -73,6 +77,15 @@ Args::Args(const QApplication &app)
this->printVersion = parser.isSet("V"); this->printVersion = parser.isSet("V");
this->crashRecovery = parser.isSet("crash-recovery"); this->crashRecovery = parser.isSet("crash-recovery");
if (parser.isSet(parentWindowIdOption))
{
this->isFramelessEmbed = true;
this->dontSaveSettings = true;
this->dontLoadMainWindow = true;
this->parentWindowId = parser.value(parentWindowIdOption).toULongLong();
}
} }
void Args::applyCustomChannelLayout(const QString &argValue) void Args::applyCustomChannelLayout(const QString &argValue)

View file

@ -15,7 +15,13 @@ public:
bool printVersion{}; bool printVersion{};
bool crashRecovery{}; bool crashRecovery{};
bool shouldRunBrowserExtensionHost{}; bool shouldRunBrowserExtensionHost{};
// Shows a single chat. Used on windows to embed in another application.
bool isFramelessEmbed{};
boost::optional<unsigned long long> parentWindowId{};
// Not settings directly
bool dontSaveSettings{}; bool dontSaveSettings{};
bool dontLoadMainWindow{};
boost::optional<WindowLayout> customChannelLayout; boost::optional<WindowLayout> customChannelLayout;
bool verbose{}; bool verbose{};

View file

@ -28,6 +28,7 @@
#include "util/Clamp.hpp" #include "util/Clamp.hpp"
#include "util/CombinePath.hpp" #include "util/CombinePath.hpp"
#include "widgets/AccountSwitchPopup.hpp" #include "widgets/AccountSwitchPopup.hpp"
#include "widgets/FramelessEmbedWindow.hpp"
#include "widgets/Notebook.hpp" #include "widgets/Notebook.hpp"
#include "widgets/Window.hpp" #include "widgets/Window.hpp"
#include "widgets/dialogs/SettingsDialog.hpp" #include "widgets/dialogs/SettingsDialog.hpp"
@ -130,6 +131,8 @@ WindowManager::WindowManager()
}); });
} }
WindowManager::~WindowManager() = default;
MessageElementFlags WindowManager::getWordFlags() MessageElementFlags WindowManager::getWordFlags()
{ {
return this->wordFlags_; return this->wordFlags_;
@ -269,22 +272,14 @@ Window &WindowManager::createWindow(WindowType type, bool show)
return *window; return *window;
} }
int WindowManager::windowCount() void WindowManager::select(Split *split)
{ {
return this->windows_.size(); this->selectSplit.invoke(split);
} }
Window *WindowManager::windowAt(int index) void WindowManager::select(SplitContainer *container)
{ {
assertInGuiThread(); this->selectSplitContainer.invoke(container);
if (index < 0 || (size_t)index >= this->windows_.size())
{
return nullptr;
}
qCDebug(chatterinoWindowmanager) << "getting window at bad index" << index;
return this->windows_.at(index);
} }
QPoint WindowManager::emotePopupPos() QPoint WindowManager::emotePopupPos()
@ -324,11 +319,23 @@ void WindowManager::initialize(Settings &settings, Paths &paths)
this->applyWindowLayout(windowLayout); this->applyWindowLayout(windowLayout);
} }
if (getArgs().isFramelessEmbed)
{
this->framelessEmbedWindow_.reset(new FramelessEmbedWindow);
this->framelessEmbedWindow_->show();
}
// No main window has been created from loading, create an empty one // No main window has been created from loading, create an empty one
if (this->mainWindow_ == nullptr) if (this->mainWindow_ == nullptr)
{ {
this->mainWindow_ = &this->createWindow(WindowType::Main); this->mainWindow_ = &this->createWindow(WindowType::Main);
this->mainWindow_->getNotebook().addPage(true); this->mainWindow_->getNotebook().addPage(true);
// TODO: don't create main window if it's a frameless embed
if (getArgs().isFramelessEmbed)
{
this->mainWindow_->hide();
}
} }
settings.timestampFormat.connect([this](auto, auto) { settings.timestampFormat.connect([this](auto, auto) {
@ -634,6 +641,11 @@ WindowLayout WindowManager::loadWindowLayoutFromFile() const
void WindowManager::applyWindowLayout(const WindowLayout &layout) void WindowManager::applyWindowLayout(const WindowLayout &layout)
{ {
if (getArgs().dontLoadMainWindow)
{
return;
}
// Set emote popup position // Set emote popup position
this->emotePopupPos_ = layout.emotePopupPos_; this->emotePopupPos_ = layout.emotePopupPos_;

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <memory>
#include "common/Channel.hpp" #include "common/Channel.hpp"
#include "common/FlagsEnum.hpp" #include "common/FlagsEnum.hpp"
#include "common/Singleton.hpp" #include "common/Singleton.hpp"
@ -19,6 +20,7 @@ using MessageElementFlags = FlagsEnum<MessageElementFlag>;
enum class WindowType; enum class WindowType;
enum class SettingsDialogPreference; enum class SettingsDialogPreference;
class FramelessEmbedWindow;
class WindowManager final : public Singleton class WindowManager final : public Singleton
{ {
@ -26,6 +28,7 @@ public:
static const QString WINDOW_LAYOUT_FILENAME; static const QString WINDOW_LAYOUT_FILENAME;
WindowManager(); WindowManager();
~WindowManager() override;
static void encodeChannel(IndirectChannel channel, QJsonObject &obj); static void encodeChannel(IndirectChannel channel, QJsonObject &obj);
static void encodeFilters(Split *split, QJsonArray &arr); static void encodeFilters(Split *split, QJsonArray &arr);
@ -53,8 +56,8 @@ public:
Window &getSelectedWindow(); Window &getSelectedWindow();
Window &createWindow(WindowType type, bool show = true); Window &createWindow(WindowType type, bool show = true);
int windowCount(); void select(Split *split);
Window *windowAt(int index); void select(SplitContainer *container);
QPoint emotePopupPos(); QPoint emotePopupPos();
void setEmotePopupPos(QPoint pos); void setEmotePopupPos(QPoint pos);
@ -92,6 +95,9 @@ public:
// It is currently being used by the "Tooltip Preview Image" system to recheck if an image is ready to be rendered. // It is currently being used by the "Tooltip Preview Image" system to recheck if an image is ready to be rendered.
pajlada::Signals::NoArgSignal miscUpdate; pajlada::Signals::NoArgSignal miscUpdate;
pajlada::Signals::Signal<Split *> selectSplit;
pajlada::Signals::Signal<SplitContainer *> selectSplitContainer;
private: private:
void encodeNodeRecursively(SplitContainer::Node *node, QJsonObject &obj); void encodeNodeRecursively(SplitContainer::Node *node, QJsonObject &obj);
@ -112,6 +118,7 @@ private:
std::vector<Window *> windows_; std::vector<Window *> windows_;
std::unique_ptr<FramelessEmbedWindow> framelessEmbedWindow_;
Window *mainWindow_{}; Window *mainWindow_{};
Window *selectedWindow_{}; Window *selectedWindow_{};

View file

@ -0,0 +1,100 @@
#include "FramelessEmbedWindow.hpp"
#include <QHBoxLayout>
#include "Application.hpp"
#include "QJsonDocument"
#include "QMessageBox"
#include "providers/twitch/TwitchIrcServer.hpp"
//#include "widgets/helper/ChannelView.hpp"
#include "common/Args.hpp"
#include "widgets/splits/Split.hpp"
#ifdef USEWINSDK
# include "Windows.h"
#endif
namespace chatterino {
FramelessEmbedWindow::FramelessEmbedWindow()
: BaseWindow(BaseWindow::Frameless)
{
this->split_ = new Split((QWidget *)nullptr);
auto layout = new QHBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(this->split_);
this->getLayoutContainer()->setLayout(layout);
}
#ifdef USEWINSDK
bool FramelessEmbedWindow::nativeEvent(const QByteArray &eventType,
void *message, long *result)
{
# if (QT_VERSION == QT_VERSION_CHECK(5, 11, 1))
MSG *msg = *reinterpret_cast<MSG **>(message);
# else
MSG *msg = reinterpret_cast<MSG *>(message);
# endif
if (msg->message == WM_COPYDATA)
{
auto data = reinterpret_cast<COPYDATASTRUCT *>(msg->lParam);
// no idea why I have to read it to a string and then encode it back to utf-8
auto str = QString::fromUtf8(reinterpret_cast<char *>(data->lpData),
int(data->cbData));
auto doc = QJsonDocument::fromJson(str.toUtf8());
auto root = doc.object();
if (root.value("type").toString() == "set-channel")
{
if (root.value("provider").toString() == "twitch")
{
auto channelName = root.value("channel-name").toString();
this->split_->setChannel(
getApp()->twitch2->getOrAddChannel(channelName));
}
}
}
return BaseWidget::nativeEvent(eventType, message, result);
}
void FramelessEmbedWindow::showEvent(QShowEvent *)
{
if (!getArgs().parentWindowId)
{
return;
}
if (auto parentHwnd =
reinterpret_cast<HWND>(getArgs().parentWindowId.get()))
{
auto handle = reinterpret_cast<HWND>(this->winId());
if (!::SetParent(handle, parentHwnd))
{
qApp->exit(1);
}
QJsonDocument doc;
QJsonObject root;
root.insert("type", "created-window");
root.insert(
"window-id",
QString::number(reinterpret_cast<unsigned long long>(handle)));
doc.setObject(root);
auto json = doc.toJson();
json.append('\0');
COPYDATASTRUCT cds;
cds.cbData = static_cast<DWORD>(json.size());
cds.lpData = json.data();
::SendMessage(parentHwnd, WM_COPYDATA, reinterpret_cast<WPARAM>(handle),
reinterpret_cast<LPARAM>(&cds));
}
}
#endif
} // namespace chatterino

View file

@ -0,0 +1,25 @@
#pragma once
#include "widgets/BaseWindow.hpp"
namespace chatterino {
class Split;
class FramelessEmbedWindow : public BaseWindow
{
public:
FramelessEmbedWindow();
protected:
#ifdef USEWINSDK
bool nativeEvent(const QByteArray &eventType, void *message,
long *result) override;
void showEvent(QShowEvent *event) override;
#endif
private:
Split *split_{};
};
} // namespace chatterino

View file

@ -631,6 +631,29 @@ SplitNotebook::SplitNotebook(Window *parent)
{ {
this->addCustomButtons(); this->addCustomButtons();
} }
this->signalHolder_.managedConnect(
getApp()->windows->selectSplit, [this](Split *split) {
for (auto &&item : this->items())
{
if (auto sc = dynamic_cast<SplitContainer *>(item.page))
{
auto &&splits = sc->getSplits();
if (std::find(splits.begin(), splits.end(), split) !=
splits.end())
{
this->select(item.page);
split->setFocus();
break;
}
}
}
});
this->signalHolder_.managedConnect(getApp()->windows->selectSplitContainer,
[this](SplitContainer *sc) {
this->select(sc);
});
} }
void SplitNotebook::showEvent(QShowEvent *) void SplitNotebook::showEvent(QShowEvent *)

View file

@ -64,13 +64,18 @@ protected:
NotebookButton *getAddButton(); NotebookButton *getAddButton();
NotebookButton *addCustomButton(); NotebookButton *addCustomButton();
private:
struct Item { struct Item {
NotebookTab *tab{}; NotebookTab *tab{};
QWidget *page{}; QWidget *page{};
QWidget *selectedWidget{}; QWidget *selectedWidget{};
}; };
const QList<Item> items()
{
return items_;
}
private:
bool containsPage(QWidget *page); bool containsPage(QWidget *page);
Item &findItem(QWidget *page); Item &findItem(QWidget *page);

View file

@ -94,7 +94,7 @@ void QuickSwitcherPopup::updateSuggestions(const QString &text)
if (split->getChannel()->getName().contains(text, if (split->getChannel()->getName().contains(text,
Qt::CaseInsensitive)) Qt::CaseInsensitive))
{ {
auto item = std::make_unique<SwitchSplitItem>(split); auto item = std::make_unique<SwitchSplitItem>(sc, split);
this->switcherModel_.addItem(std::move(item)); this->switcherModel_.addItem(std::move(item));
// We want to continue the outer loop so we need a goto // We want to continue the outer loop so we need a goto

View file

@ -7,31 +7,23 @@
namespace chatterino { namespace chatterino {
SwitchSplitItem::SwitchSplitItem(Split *split) SwitchSplitItem::SwitchSplitItem(SplitContainer *container, Split *split)
: AbstractSwitcherItem(QIcon(":switcher/switch.svg"))
, split_(split)
, container_(split->getContainer())
{
}
SwitchSplitItem::SwitchSplitItem(SplitContainer *container)
: AbstractSwitcherItem(QIcon(":switcher/switch.svg")) : AbstractSwitcherItem(QIcon(":switcher/switch.svg"))
, container_(container) , container_(container)
, split_(split)
{ {
assert(this->container_ != nullptr);
} }
void SwitchSplitItem::action() void SwitchSplitItem::action()
{ {
auto &nb = getApp()->windows->getMainWindow().getNotebook();
nb.select(this->container_);
/*
* If the item is referring to a specific channel, select the
* corresponding split.
*/
if (this->split_) if (this->split_)
{ {
this->container_->setSelected(this->split_); getApp()->windows->select(this->split_);
}
else if (this->container_)
{
getApp()->windows->select(this->container_);
} }
} }

View file

@ -13,8 +13,7 @@ namespace chatterino {
class SwitchSplitItem : public AbstractSwitcherItem class SwitchSplitItem : public AbstractSwitcherItem
{ {
public: public:
SwitchSplitItem(Split *split); SwitchSplitItem(SplitContainer *container, Split *split = nullptr);
SwitchSplitItem(SplitContainer *container);
virtual void action() override; virtual void action() override;
@ -22,8 +21,8 @@ public:
virtual QSize sizeHint(const QRect &rect) const override; virtual QSize sizeHint(const QRect &rect) const override;
private: private:
Split *split_{};
SplitContainer *container_{}; SplitContainer *container_{};
Split *split_{};
}; };
} // namespace chatterino } // namespace chatterino

View file

@ -31,7 +31,6 @@
#include "widgets/helper/NotebookTab.hpp" #include "widgets/helper/NotebookTab.hpp"
#include "widgets/helper/ResizingTextEdit.hpp" #include "widgets/helper/ResizingTextEdit.hpp"
#include "widgets/helper/SearchPopup.hpp" #include "widgets/helper/SearchPopup.hpp"
#include "widgets/splits/ClosedSplits.hpp"
#include "widgets/splits/SplitContainer.hpp" #include "widgets/splits/SplitContainer.hpp"
#include "widgets/splits/SplitHeader.hpp" #include "widgets/splits/SplitHeader.hpp"
#include "widgets/splits/SplitInput.hpp" #include "widgets/splits/SplitInput.hpp"
@ -78,15 +77,8 @@ namespace {
pajlada::Signals::Signal<Qt::KeyboardModifiers> Split::modifierStatusChanged; pajlada::Signals::Signal<Qt::KeyboardModifiers> Split::modifierStatusChanged;
Qt::KeyboardModifiers Split::modifierStatus = Qt::NoModifier; Qt::KeyboardModifiers Split::modifierStatus = Qt::NoModifier;
Split::Split(SplitContainer *parent)
: Split(static_cast<QWidget *>(parent))
{
this->container_ = parent;
}
Split::Split(QWidget *parent) Split::Split(QWidget *parent)
: BaseWidget(parent) : BaseWidget(parent)
, container_(nullptr)
, channel_(Channel::getEmpty()) , channel_(Channel::getEmpty())
, vbox_(new QVBoxLayout(this)) , vbox_(new QVBoxLayout(this))
, header_(new SplitHeader(this)) , header_(new SplitHeader(this))
@ -173,7 +165,7 @@ Split::Split(QWidget *parent)
}); });
this->view_->joinToChannel.connect([this](QString twitchChannel) { this->view_->joinToChannel.connect([this](QString twitchChannel) {
this->container_->appendNewSplit(false)->setChannel( this->openSplitRequested.invoke(
getApp()->twitch.server->getOrAddChannel(twitchChannel)); getApp()->twitch.server->getOrAddChannel(twitchChannel));
}); });
@ -294,21 +286,6 @@ ChannelView &Split::getChannelView()
return *this->view_; return *this->view_;
} }
SplitContainer *Split::getContainer()
{
return this->container_;
}
bool Split::isInContainer() const
{
return this->container_ != nullptr;
}
void Split::setContainer(SplitContainer *container)
{
this->container_ = container;
}
void Split::updateInputPlaceholder() void Split::updateInputPlaceholder()
{ {
if (!this->getChannel()->isTwitchChannel()) if (!this->getChannel()->isTwitchChannel())
@ -388,7 +365,7 @@ void Split::setChannel(IndirectChannel newChannel)
} }
this->channel_.get()->displayNameChanged.connect([this] { this->channel_.get()->displayNameChanged.connect([this] {
this->container_->refreshTab(); this->actionRequested.invoke(Action::RefreshTab);
}); });
this->channelChanged.invoke(); this->channelChanged.invoke();
@ -435,10 +412,7 @@ void Split::showChangeChannelPopup(const char *dialogTitle, bool empty,
if (dialog->hasSeletedChannel()) if (dialog->hasSeletedChannel())
{ {
this->setChannel(dialog->getSelectedChannel()); this->setChannel(dialog->getSelectedChannel());
if (this->isInContainer()) this->actionRequested.invoke(Action::RefreshTab);
{
this->container_->refreshTab();
}
} }
callback(dialog->hasSeletedChannel()); callback(dialog->hasSeletedChannel());
@ -514,10 +488,7 @@ void Split::enterEvent(QEvent *event)
this->overlay_->show(); this->overlay_->show();
} }
if (this->container_ != nullptr) this->actionRequested.invoke(Action::ResetMouseStatus);
{
this->container_->resetMouseStatus();
}
} }
void Split::leaveEvent(QEvent *event) void Split::leaveEvent(QEvent *event)
@ -554,23 +525,12 @@ void Split::setIsTopRightSplit(bool value)
/// Slots /// Slots
void Split::addSibling() void Split::addSibling()
{ {
if (this->container_) this->actionRequested.invoke(Action::AppendNewSplit);
{
this->container_->appendNewSplit(true);
}
} }
void Split::deleteFromContainer() void Split::deleteFromContainer()
{ {
if (this->container_) this->actionRequested.invoke(Action::Delete);
{
this->container_->deleteSplit(this);
auto *tab = this->getContainer()->getTab();
tab->connect(tab, &QWidget::destroyed, [tab]() mutable {
ClosedSplits::invalidateTab(tab);
});
ClosedSplits::push({this->getChannel()->getName(), tab});
}
} }
void Split::changeChannel() void Split::changeChannel()

View file

@ -38,7 +38,6 @@ class Split : public BaseWidget, pajlada::Signals::SignalHolder
Q_OBJECT Q_OBJECT
public: public:
explicit Split(SplitContainer *parent);
explicit Split(QWidget *parent); explicit Split(QWidget *parent);
~Split() override; ~Split() override;
@ -48,7 +47,6 @@ public:
pajlada::Signals::NoArgSignal focusLost; pajlada::Signals::NoArgSignal focusLost;
ChannelView &getChannelView(); ChannelView &getChannelView();
SplitContainer *getContainer();
IndirectChannel getIndirectChannel(); IndirectChannel getIndirectChannel();
ChannelPtr getChannel(); ChannelPtr getChannel();
@ -80,6 +78,24 @@ public:
modifierStatusChanged; modifierStatusChanged;
static Qt::KeyboardModifiers modifierStatus; static Qt::KeyboardModifiers modifierStatus;
enum class Action {
RefreshTab,
ResetMouseStatus,
AppendNewSplit,
Delete,
SelectSplitLeft,
SelectSplitRight,
SelectSplitAbove,
SelectSplitBelow,
};
pajlada::Signals::Signal<Action> actionRequested;
pajlada::Signals::Signal<ChannelPtr> openSplitRequested;
// args: (SplitContainer::Direction dir, Split* parent)
pajlada::Signals::Signal<int, Split *> insertSplitRequested;
protected: protected:
void paintEvent(QPaintEvent *event) override; void paintEvent(QPaintEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override;
@ -98,7 +114,6 @@ private:
void handleModifiers(Qt::KeyboardModifiers modifiers); void handleModifiers(Qt::KeyboardModifiers modifiers);
void updateInputPlaceholder(); void updateInputPlaceholder();
SplitContainer *container_;
IndirectChannel channel_; IndirectChannel channel_;
bool moderationMode_{}; bool moderationMode_{};

View file

@ -11,6 +11,7 @@
#include "widgets/Notebook.hpp" #include "widgets/Notebook.hpp"
#include "widgets/helper/ChannelView.hpp" #include "widgets/helper/ChannelView.hpp"
#include "widgets/helper/NotebookTab.hpp" #include "widgets/helper/NotebookTab.hpp"
#include "widgets/splits/ClosedSplits.hpp"
#include "widgets/splits/Split.hpp" #include "widgets/splits/Split.hpp"
#include <QApplication> #include <QApplication>
@ -160,8 +161,6 @@ void SplitContainer::insertSplit(Split *split, Direction direction,
assertInGuiThread(); assertInGuiThread();
split->setContainer(this);
if (relativeTo == nullptr) if (relativeTo == nullptr)
{ {
if (this->baseNode_.type_ == Node::EmptyRoot) if (this->baseNode_.type_ == Node::EmptyRoot)
@ -211,7 +210,9 @@ void SplitContainer::addSplit(Split *split)
this->refreshTab(); this->refreshTab();
this->managedConnect(split->getChannelView().tabHighlightRequested, auto &&conns = this->connectionsPerSplit_[split];
conns.managedConnect(split->getChannelView().tabHighlightRequested,
[this](HighlightState state) { [this](HighlightState state) {
if (this->tab_ != nullptr) if (this->tab_ != nullptr)
{ {
@ -219,14 +220,64 @@ void SplitContainer::addSplit(Split *split)
} }
}); });
this->managedConnect(split->getChannelView().liveStatusChanged, [this]() { conns.managedConnect(split->getChannelView().liveStatusChanged, [this]() {
this->refreshTabLiveStatus(); this->refreshTabLiveStatus();
}); });
this->managedConnect(split->focused, [this, split] { conns.managedConnect(split->focused, [this, split] {
this->setSelected(split); this->setSelected(split);
}); });
conns.managedConnect(split->openSplitRequested, [this](auto channel) {
this->appendNewSplit(false)->setChannel(channel);
});
conns.managedConnect(
split->actionRequested, [this, split](Split::Action action) {
switch (action)
{
case Split::Action::RefreshTab:
this->refreshTab();
break;
case Split::Action::ResetMouseStatus:
this->resetMouseStatus();
break;
case Split::Action::AppendNewSplit:
this->appendNewSplit(true);
break;
case Split::Action::Delete: {
this->deleteSplit(split);
auto *tab = this->getTab();
tab->connect(tab, &QWidget::destroyed, [tab]() mutable {
ClosedSplits::invalidateTab(tab);
});
ClosedSplits::push({split->getChannel()->getName(), tab});
}
break;
case Split::Action::SelectSplitLeft:
this->selectNextSplit(SplitContainer::Left);
break;
case Split::Action::SelectSplitRight:
this->selectNextSplit(SplitContainer::Right);
break;
case Split::Action::SelectSplitAbove:
this->selectNextSplit(SplitContainer::Above);
break;
case Split::Action::SelectSplitBelow:
this->selectNextSplit(SplitContainer::Below);
break;
}
});
conns.managedConnect(split->insertSplitRequested, [this](int dir,
Split *parent) {
this->insertSplit(new Split(this), static_cast<Direction>(dir), parent);
});
this->layout(); this->layout();
} }
@ -282,10 +333,7 @@ SplitContainer::Position SplitContainer::releaseSplit(Split *split)
this->refreshTab(); this->refreshTab();
// fourtf: really bad this->connectionsPerSplit_.erase(this->connectionsPerSplit_.find(split));
split->getChannelView().tabHighlightRequested.disconnectAll();
split->getChannelView().tabHighlightRequested.disconnectAll();
return position; return position;
} }

View file

@ -255,6 +255,9 @@ private:
NotebookTab *tab_; NotebookTab *tab_;
std::vector<Split *> splits_; std::vector<Split *> splits_;
std::unordered_map<Split *, pajlada::Signals::SignalHolder>
connectionsPerSplit_;
bool isDragging_ = false; bool isDragging_ = false;
}; };

View file

@ -247,12 +247,8 @@ void SplitInput::installKeyPressedEvent()
} }
if (event->modifiers() == Qt::AltModifier) if (event->modifiers() == Qt::AltModifier)
{ {
SplitContainer *page = this->split_->getContainer(); this->split_->actionRequested.invoke(
Split::Action::SelectSplitAbove);
if (page != nullptr)
{
page->selectNextSplit(SplitContainer::Above);
}
} }
else else
{ {
@ -312,49 +308,37 @@ void SplitInput::installKeyPressedEvent()
event->modifiers() == Qt::AltModifier) event->modifiers() == Qt::AltModifier)
{ {
// h: vim binding for left // h: vim binding for left
SplitContainer *page = this->split_->getContainer(); this->split_->actionRequested.invoke(
event->accept(); Split::Action::SelectSplitLeft);
if (page != nullptr) event->accept();
{
page->selectNextSplit(SplitContainer::Left);
}
} }
else if (event->key() == Qt::Key_J && else if (event->key() == Qt::Key_J &&
event->modifiers() == Qt::AltModifier) event->modifiers() == Qt::AltModifier)
{ {
// j: vim binding for down // j: vim binding for down
SplitContainer *page = this->split_->getContainer(); this->split_->actionRequested.invoke(
event->accept(); Split::Action::SelectSplitBelow);
if (page != nullptr) event->accept();
{
page->selectNextSplit(SplitContainer::Below);
}
} }
else if (event->key() == Qt::Key_K && else if (event->key() == Qt::Key_K &&
event->modifiers() == Qt::AltModifier) event->modifiers() == Qt::AltModifier)
{ {
// k: vim binding for up // k: vim binding for up
SplitContainer *page = this->split_->getContainer(); this->split_->actionRequested.invoke(
event->accept(); Split::Action::SelectSplitAbove);
if (page != nullptr) event->accept();
{
page->selectNextSplit(SplitContainer::Above);
}
} }
else if (event->key() == Qt::Key_L && else if (event->key() == Qt::Key_L &&
event->modifiers() == Qt::AltModifier) event->modifiers() == Qt::AltModifier)
{ {
// l: vim binding for right // l: vim binding for right
SplitContainer *page = this->split_->getContainer(); this->split_->actionRequested.invoke(
event->accept(); Split::Action::SelectSplitRight);
if (page != nullptr) event->accept();
{
page->selectNextSplit(SplitContainer::Right);
}
} }
else if (event->key() == Qt::Key_Down) else if (event->key() == Qt::Key_Down)
{ {
@ -364,12 +348,8 @@ void SplitInput::installKeyPressedEvent()
} }
if (event->modifiers() == Qt::AltModifier) if (event->modifiers() == Qt::AltModifier)
{ {
SplitContainer *page = this->split_->getContainer(); this->split_->actionRequested.invoke(
Split::Action::SelectSplitBelow);
if (page != nullptr)
{
page->selectNextSplit(SplitContainer::Below);
}
} }
else else
{ {
@ -422,24 +402,16 @@ void SplitInput::installKeyPressedEvent()
{ {
if (event->modifiers() == Qt::AltModifier) if (event->modifiers() == Qt::AltModifier)
{ {
SplitContainer *page = this->split_->getContainer(); this->split_->actionRequested.invoke(
Split::Action::SelectSplitLeft);
if (page != nullptr)
{
page->selectNextSplit(SplitContainer::Left);
}
} }
} }
else if (event->key() == Qt::Key_Right) else if (event->key() == Qt::Key_Right)
{ {
if (event->modifiers() == Qt::AltModifier) if (event->modifiers() == Qt::AltModifier)
{ {
SplitContainer *page = this->split_->getContainer(); this->split_->actionRequested.invoke(
Split::Action::SelectSplitRight);
if (page != nullptr)
{
page->selectNextSplit(SplitContainer::Right);
}
} }
} }
else if ((event->key() == Qt::Key_C || else if ((event->key() == Qt::Key_C ||

View file

@ -220,18 +220,13 @@ bool SplitOverlay::ButtonEventFilter::eventFilter(QObject *watched,
case QEvent::MouseButtonRelease: { case QEvent::MouseButtonRelease: {
if (this->hoveredElement != HoveredElement::SplitMove) if (this->hoveredElement != HoveredElement::SplitMove)
{ {
SplitContainer *container = auto dir = SplitContainer::Direction(
this->parent->split_->getContainer(); this->hoveredElement + SplitContainer::Left - SplitLeft);
if (container != nullptr) this->parent->split_->insertSplitRequested.invoke(
{ static_cast<int>(dir), this->parent->split_);
auto *_split = new Split(container);
auto dir = SplitContainer::Direction(this->hoveredElement + this->parent->hide();
SplitContainer::Left -
SplitLeft);
container->insertSplit(_split, dir, this->parent->split_);
this->parent->hide();
}
} }
} }
break; break;