2018-07-15 14:03:41 +02:00
|
|
|
#include "singletons/WindowManager.hpp"
|
2018-06-26 14:09:39 +02:00
|
|
|
|
2019-09-22 15:32:36 +02:00
|
|
|
#include <QDebug>
|
|
|
|
#include <QDesktopWidget>
|
|
|
|
#include <QJsonArray>
|
|
|
|
#include <QJsonDocument>
|
|
|
|
#include <QJsonObject>
|
|
|
|
#include <QMessageBox>
|
|
|
|
#include <QSaveFile>
|
|
|
|
#include <QScreen>
|
|
|
|
#include <boost/optional.hpp>
|
|
|
|
#include <chrono>
|
|
|
|
|
2021-03-13 11:23:20 +01:00
|
|
|
#include <QMessageBox>
|
2018-06-26 14:09:39 +02:00
|
|
|
#include "Application.hpp"
|
2020-09-26 16:03:51 +02:00
|
|
|
#include "common/Args.hpp"
|
2020-11-21 16:20:10 +01:00
|
|
|
#include "common/QLogging.hpp"
|
2018-06-26 17:20:03 +02:00
|
|
|
#include "debug/AssertInGuiThread.hpp"
|
2018-08-11 22:23:06 +02:00
|
|
|
#include "messages/MessageElement.hpp"
|
2019-09-11 00:10:49 +02:00
|
|
|
#include "providers/irc/Irc2.hpp"
|
|
|
|
#include "providers/irc/IrcChannel2.hpp"
|
|
|
|
#include "providers/irc/IrcServer.hpp"
|
2019-09-15 13:02:02 +02:00
|
|
|
#include "providers/twitch/TwitchIrcServer.hpp"
|
2018-06-28 19:46:45 +02:00
|
|
|
#include "singletons/Fonts.hpp"
|
|
|
|
#include "singletons/Paths.hpp"
|
2018-08-11 22:23:06 +02:00
|
|
|
#include "singletons/Settings.hpp"
|
2018-06-28 20:03:04 +02:00
|
|
|
#include "singletons/Theme.hpp"
|
2018-06-26 14:09:39 +02:00
|
|
|
#include "util/Clamp.hpp"
|
2020-09-19 17:14:10 +02:00
|
|
|
#include "util/CombinePath.hpp"
|
2019-09-01 14:13:44 +02:00
|
|
|
#include "widgets/AccountSwitchPopup.hpp"
|
2021-04-17 16:15:23 +02:00
|
|
|
#include "widgets/FramelessEmbedWindow.hpp"
|
2018-08-11 22:23:06 +02:00
|
|
|
#include "widgets/Notebook.hpp"
|
|
|
|
#include "widgets/Window.hpp"
|
2018-06-26 15:11:45 +02:00
|
|
|
#include "widgets/dialogs/SettingsDialog.hpp"
|
2018-08-11 22:23:06 +02:00
|
|
|
#include "widgets/helper/NotebookTab.hpp"
|
|
|
|
#include "widgets/splits/Split.hpp"
|
|
|
|
#include "widgets/splits/SplitContainer.hpp"
|
2017-01-15 16:38:30 +01:00
|
|
|
|
2017-01-18 21:30:23 +01:00
|
|
|
namespace chatterino {
|
2019-08-25 21:23:54 +02:00
|
|
|
namespace {
|
2020-09-19 17:14:10 +02:00
|
|
|
|
2019-08-25 22:58:19 +02:00
|
|
|
boost::optional<bool> &shouldMoveOutOfBoundsWindow()
|
2019-08-25 21:23:54 +02:00
|
|
|
{
|
2019-08-25 22:58:19 +02:00
|
|
|
static boost::optional<bool> x;
|
2019-08-25 21:23:54 +02:00
|
|
|
return x;
|
|
|
|
}
|
2020-09-19 17:14:10 +02:00
|
|
|
|
2019-08-25 21:23:54 +02:00
|
|
|
} // namespace
|
2018-01-05 02:56:18 +01:00
|
|
|
|
2021-01-23 16:26:42 +01:00
|
|
|
const QString WindowManager::WINDOW_LAYOUT_FILENAME(
|
|
|
|
QStringLiteral("window-layout.json"));
|
|
|
|
|
2018-06-26 17:06:17 +02:00
|
|
|
using SplitNode = SplitContainer::Node;
|
|
|
|
using SplitDirection = SplitContainer::Direction;
|
2018-05-16 14:55:45 +02:00
|
|
|
|
2020-10-31 16:42:48 +01:00
|
|
|
void WindowManager::showSettingsDialog(QWidget *parent,
|
|
|
|
SettingsDialogPreference preference)
|
2018-01-24 15:08:22 +01:00
|
|
|
{
|
2021-03-13 11:23:20 +01:00
|
|
|
if (getArgs().dontSaveSettings)
|
|
|
|
{
|
|
|
|
QMessageBox::critical(parent, "Chatterino - Editing Settings Forbidden",
|
|
|
|
"Settings cannot be edited when running with "
|
|
|
|
"commandline arguments such as '-c'.");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
QTimer::singleShot(80, [parent, preference] {
|
|
|
|
SettingsDialog::showDialog(parent, preference);
|
|
|
|
});
|
|
|
|
}
|
2018-01-24 15:08:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void WindowManager::showAccountSelectPopup(QPoint point)
|
|
|
|
{
|
|
|
|
// static QWidget *lastFocusedWidget = nullptr;
|
2019-09-01 14:13:44 +02:00
|
|
|
static AccountSwitchPopup *w = new AccountSwitchPopup();
|
2018-01-24 15:08:22 +01:00
|
|
|
|
2018-10-21 13:43:02 +02:00
|
|
|
if (w->hasFocus())
|
|
|
|
{
|
2018-01-24 15:08:22 +01:00
|
|
|
w->hide();
|
|
|
|
// if (lastFocusedWidget) {
|
|
|
|
// lastFocusedWidget->setFocus();
|
|
|
|
// }
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// lastFocusedWidget = this->focusWidget();
|
|
|
|
|
|
|
|
w->refresh();
|
|
|
|
|
|
|
|
QPoint buttonPos = point;
|
2019-09-08 12:43:12 +02:00
|
|
|
w->move(buttonPos.x() - 30, buttonPos.y());
|
2018-01-24 15:08:22 +01:00
|
|
|
w->show();
|
|
|
|
w->setFocus();
|
|
|
|
}
|
|
|
|
|
2018-04-27 22:11:19 +02:00
|
|
|
WindowManager::WindowManager()
|
2021-01-23 16:26:42 +01:00
|
|
|
: windowLayoutFilePath(combinePath(getPaths()->settingsDirectory,
|
|
|
|
WindowManager::WINDOW_LAYOUT_FILENAME))
|
2017-04-13 19:25:33 +02:00
|
|
|
{
|
2020-11-21 16:20:10 +01:00
|
|
|
qCDebug(chatterinoWindowmanager) << "init WindowManager";
|
2018-06-28 19:38:57 +02:00
|
|
|
|
|
|
|
auto settings = getSettings();
|
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
this->wordFlagsListener_.addSetting(settings->showTimestamps);
|
2018-09-16 17:42:30 +02:00
|
|
|
this->wordFlagsListener_.addSetting(settings->showBadgesGlobalAuthority);
|
2021-04-25 14:37:19 +02:00
|
|
|
this->wordFlagsListener_.addSetting(settings->showBadgesPredictions);
|
2018-09-16 17:42:30 +02:00
|
|
|
this->wordFlagsListener_.addSetting(settings->showBadgesChannelAuthority);
|
|
|
|
this->wordFlagsListener_.addSetting(settings->showBadgesSubscription);
|
|
|
|
this->wordFlagsListener_.addSetting(settings->showBadgesVanity);
|
|
|
|
this->wordFlagsListener_.addSetting(settings->showBadgesChatterino);
|
2020-10-25 10:36:00 +01:00
|
|
|
this->wordFlagsListener_.addSetting(settings->showBadgesFfz);
|
2019-06-22 14:34:54 +02:00
|
|
|
this->wordFlagsListener_.addSetting(settings->enableEmoteImages);
|
2018-10-31 19:45:51 +01:00
|
|
|
this->wordFlagsListener_.addSetting(settings->boldUsernames);
|
|
|
|
this->wordFlagsListener_.addSetting(settings->lowercaseDomains);
|
2019-01-22 22:15:38 +01:00
|
|
|
this->wordFlagsListener_.setCB([this] {
|
2020-11-08 12:02:19 +01:00
|
|
|
this->updateWordTypeMask();
|
2019-01-22 22:15:38 +01:00
|
|
|
});
|
2018-10-07 18:27:40 +02:00
|
|
|
|
|
|
|
this->saveTimer = new QTimer;
|
|
|
|
|
|
|
|
this->saveTimer->setSingleShot(true);
|
|
|
|
|
|
|
|
QObject::connect(this->saveTimer, &QTimer::timeout, [] {
|
2020-11-08 12:02:19 +01:00
|
|
|
getApp()->windows->save();
|
2018-10-07 18:27:40 +02:00
|
|
|
});
|
2020-02-15 17:16:10 +01:00
|
|
|
|
|
|
|
this->miscUpdateTimer_.start(100);
|
|
|
|
|
|
|
|
QObject::connect(&this->miscUpdateTimer_, &QTimer::timeout, [this] {
|
2020-11-08 12:02:19 +01:00
|
|
|
this->miscUpdate.invoke();
|
2020-02-15 17:16:10 +01:00
|
|
|
});
|
2018-06-28 19:38:57 +02:00
|
|
|
}
|
|
|
|
|
2021-04-17 16:15:23 +02:00
|
|
|
WindowManager::~WindowManager() = default;
|
|
|
|
|
2018-08-07 07:55:31 +02:00
|
|
|
MessageElementFlags WindowManager::getWordFlags()
|
2018-06-28 19:38:57 +02:00
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
return this->wordFlags_;
|
2018-06-28 19:38:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void WindowManager::updateWordTypeMask()
|
|
|
|
{
|
2018-08-07 07:55:31 +02:00
|
|
|
using MEF = MessageElementFlag;
|
2018-06-28 19:38:57 +02:00
|
|
|
auto settings = getSettings();
|
|
|
|
|
|
|
|
// text
|
2018-08-07 07:55:31 +02:00
|
|
|
auto flags = MessageElementFlags(MEF::Text);
|
2018-06-28 19:38:57 +02:00
|
|
|
|
|
|
|
// timestamp
|
2018-10-21 13:43:02 +02:00
|
|
|
if (settings->showTimestamps)
|
|
|
|
{
|
2018-08-07 07:55:31 +02:00
|
|
|
flags.set(MEF::Timestamp);
|
2018-06-28 19:38:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// emotes
|
2019-06-22 14:34:54 +02:00
|
|
|
if (settings->enableEmoteImages)
|
|
|
|
{
|
|
|
|
flags.set(MEF::EmoteImages);
|
|
|
|
}
|
|
|
|
flags.set(MEF::EmoteText);
|
|
|
|
flags.set(MEF::EmojiText);
|
2018-06-28 19:38:57 +02:00
|
|
|
|
|
|
|
// bits
|
2018-08-07 07:55:31 +02:00
|
|
|
flags.set(MEF::BitsAmount);
|
2018-11-14 17:26:08 +01:00
|
|
|
flags.set(settings->animateEmotes ? MEF::BitsAnimated : MEF::BitsStatic);
|
2018-06-28 19:38:57 +02:00
|
|
|
|
|
|
|
// badges
|
2018-09-16 17:42:30 +02:00
|
|
|
flags.set(settings->showBadgesGlobalAuthority ? MEF::BadgeGlobalAuthority
|
|
|
|
: MEF::None);
|
2021-04-25 14:37:19 +02:00
|
|
|
flags.set(settings->showBadgesPredictions ? MEF::BadgePredictions
|
|
|
|
: MEF::None);
|
2018-09-16 17:42:30 +02:00
|
|
|
flags.set(settings->showBadgesChannelAuthority ? MEF::BadgeChannelAuthority
|
|
|
|
: MEF::None);
|
|
|
|
flags.set(settings->showBadgesSubscription ? MEF::BadgeSubscription
|
|
|
|
: MEF::None);
|
|
|
|
flags.set(settings->showBadgesVanity ? MEF::BadgeVanity : MEF::None);
|
|
|
|
flags.set(settings->showBadgesChatterino ? MEF::BadgeChatterino
|
|
|
|
: MEF::None);
|
2020-10-25 10:36:00 +01:00
|
|
|
flags.set(settings->showBadgesFfz ? MEF::BadgeFfz : MEF::None);
|
2018-06-28 19:38:57 +02:00
|
|
|
|
|
|
|
// username
|
2018-08-07 07:55:31 +02:00
|
|
|
flags.set(MEF::Username);
|
2018-06-28 19:38:57 +02:00
|
|
|
|
|
|
|
// misc
|
2018-08-07 07:55:31 +02:00
|
|
|
flags.set(MEF::AlwaysShow);
|
|
|
|
flags.set(MEF::Collapsed);
|
2018-10-31 19:45:51 +01:00
|
|
|
flags.set(settings->boldUsernames ? MEF::BoldUsername
|
2018-11-14 17:26:08 +01:00
|
|
|
: MEF::NonBoldUsername);
|
2018-10-31 19:45:51 +01:00
|
|
|
flags.set(settings->lowercaseDomains ? MEF::LowercaseLink
|
2018-11-14 17:26:08 +01:00
|
|
|
: MEF::OriginalLink);
|
2020-08-08 15:37:22 +02:00
|
|
|
flags.set(MEF::ChannelPointReward);
|
2018-06-28 19:38:57 +02:00
|
|
|
|
|
|
|
// update flags
|
2018-08-07 07:55:31 +02:00
|
|
|
MessageElementFlags newFlags = static_cast<MessageElementFlags>(flags);
|
2018-06-28 19:38:57 +02:00
|
|
|
|
2018-10-21 13:43:02 +02:00
|
|
|
if (newFlags != this->wordFlags_)
|
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
this->wordFlags_ = newFlags;
|
2018-06-28 19:38:57 +02:00
|
|
|
|
|
|
|
this->wordFlagsChanged.invoke();
|
|
|
|
}
|
2017-04-13 19:25:33 +02:00
|
|
|
}
|
2017-01-18 21:30:23 +01:00
|
|
|
|
2018-06-04 16:10:54 +02:00
|
|
|
void WindowManager::layoutChannelViews(Channel *channel)
|
2017-01-15 16:38:30 +01:00
|
|
|
{
|
2018-11-14 17:26:08 +01:00
|
|
|
this->layoutRequested.invoke(channel);
|
2017-01-16 03:15:07 +01:00
|
|
|
}
|
|
|
|
|
2018-06-04 16:10:54 +02:00
|
|
|
void WindowManager::forceLayoutChannelViews()
|
|
|
|
{
|
|
|
|
this->incGeneration();
|
|
|
|
this->layoutChannelViews(nullptr);
|
|
|
|
}
|
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
void WindowManager::repaintVisibleChatWidgets(Channel *channel)
|
2017-01-16 03:15:07 +01:00
|
|
|
{
|
2018-11-14 17:26:08 +01:00
|
|
|
this->layoutRequested.invoke(channel);
|
2017-01-15 16:38:30 +01:00
|
|
|
}
|
2017-01-26 21:04:01 +01:00
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
void WindowManager::repaintGifEmotes()
|
2017-02-07 00:03:15 +01:00
|
|
|
{
|
2018-11-14 17:26:08 +01:00
|
|
|
this->gifRepaintRequested.invoke();
|
2017-02-07 00:03:15 +01:00
|
|
|
}
|
|
|
|
|
2017-09-16 00:05:06 +02:00
|
|
|
// void WindowManager::updateAll()
|
|
|
|
//{
|
|
|
|
// if (this->mainWindow != nullptr) {
|
|
|
|
// this->mainWindow->update();
|
|
|
|
// }
|
|
|
|
//}
|
2017-02-02 01:23:26 +01:00
|
|
|
|
2018-06-26 17:06:17 +02:00
|
|
|
Window &WindowManager::getMainWindow()
|
2017-04-13 19:25:33 +02:00
|
|
|
{
|
2018-06-26 17:06:17 +02:00
|
|
|
assertInGuiThread();
|
2018-04-26 18:10:26 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
return *this->mainWindow_;
|
2017-04-13 19:25:33 +02:00
|
|
|
}
|
|
|
|
|
2018-06-26 17:06:17 +02:00
|
|
|
Window &WindowManager::getSelectedWindow()
|
2017-11-12 17:21:50 +01:00
|
|
|
{
|
2018-06-26 17:06:17 +02:00
|
|
|
assertInGuiThread();
|
2018-04-26 18:10:26 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
return *this->selectedWindow_;
|
2017-11-12 17:21:50 +01:00
|
|
|
}
|
|
|
|
|
2019-08-26 11:26:58 +02:00
|
|
|
Window &WindowManager::createWindow(WindowType type, bool show)
|
2017-11-12 17:21:50 +01:00
|
|
|
{
|
2018-06-26 17:06:17 +02:00
|
|
|
assertInGuiThread();
|
2018-04-26 18:10:26 +02:00
|
|
|
|
2018-06-26 17:06:17 +02:00
|
|
|
auto *window = new Window(type);
|
2018-07-06 19:23:47 +02:00
|
|
|
this->windows_.push_back(window);
|
2019-08-26 11:26:58 +02:00
|
|
|
if (show)
|
|
|
|
{
|
|
|
|
window->show();
|
|
|
|
}
|
2017-11-12 17:21:50 +01:00
|
|
|
|
2018-10-21 13:43:02 +02:00
|
|
|
if (type != WindowType::Main)
|
|
|
|
{
|
2018-04-08 14:13:48 +02:00
|
|
|
window->setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
|
|
|
|
QObject::connect(window, &QWidget::destroyed, [this, window] {
|
2018-08-06 21:17:03 +02:00
|
|
|
for (auto it = this->windows_.begin(); it != this->windows_.end();
|
2018-10-21 13:43:02 +02:00
|
|
|
it++)
|
|
|
|
{
|
|
|
|
if (*it == window)
|
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
this->windows_.erase(it);
|
2018-04-08 14:13:48 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-11-12 17:21:50 +01:00
|
|
|
return *window;
|
|
|
|
}
|
|
|
|
|
2021-04-17 16:15:23 +02:00
|
|
|
void WindowManager::select(Split *split)
|
2017-12-14 00:25:06 +01:00
|
|
|
{
|
2021-04-17 16:15:23 +02:00
|
|
|
this->selectSplit.invoke(split);
|
2017-12-14 00:25:06 +01:00
|
|
|
}
|
|
|
|
|
2021-04-17 16:15:23 +02:00
|
|
|
void WindowManager::select(SplitContainer *container)
|
2017-12-14 00:25:06 +01:00
|
|
|
{
|
2021-04-17 16:15:23 +02:00
|
|
|
this->selectSplitContainer.invoke(container);
|
2017-12-14 00:25:06 +01:00
|
|
|
}
|
|
|
|
|
2020-04-13 13:15:51 +02:00
|
|
|
QPoint WindowManager::emotePopupPos()
|
|
|
|
{
|
|
|
|
return this->emotePopupPos_;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WindowManager::setEmotePopupPos(QPoint pos)
|
|
|
|
{
|
|
|
|
this->emotePopupPos_ = pos;
|
|
|
|
}
|
|
|
|
|
2018-08-02 14:23:27 +02:00
|
|
|
void WindowManager::initialize(Settings &settings, Paths &paths)
|
2017-01-26 21:04:01 +01:00
|
|
|
{
|
2018-06-26 17:06:17 +02:00
|
|
|
assertInGuiThread();
|
2018-04-26 18:10:26 +02:00
|
|
|
|
2020-11-08 12:02:19 +01:00
|
|
|
getApp()->themes->repaintVisibleChatWidgets_.connect([this] {
|
|
|
|
this->repaintVisibleChatWidgets();
|
|
|
|
});
|
2018-04-27 22:11:19 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
assert(!this->initialized_);
|
2018-04-06 23:31:34 +02:00
|
|
|
|
2018-10-21 13:43:02 +02:00
|
|
|
{
|
2021-01-23 16:26:42 +01:00
|
|
|
WindowLayout windowLayout;
|
|
|
|
|
|
|
|
if (getArgs().customChannelLayout)
|
|
|
|
{
|
|
|
|
windowLayout = getArgs().customChannelLayout.value();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
windowLayout = this->loadWindowLayoutFromFile();
|
|
|
|
}
|
2019-08-26 22:32:17 +02:00
|
|
|
|
2020-09-19 17:14:10 +02:00
|
|
|
this->emotePopupPos_ = windowLayout.emotePopupPos_;
|
2020-04-13 13:15:51 +02:00
|
|
|
|
2020-09-19 17:14:10 +02:00
|
|
|
this->applyWindowLayout(windowLayout);
|
2018-04-06 23:31:34 +02:00
|
|
|
}
|
|
|
|
|
2021-04-17 16:15:23 +02:00
|
|
|
if (getArgs().isFramelessEmbed)
|
|
|
|
{
|
|
|
|
this->framelessEmbedWindow_.reset(new FramelessEmbedWindow);
|
|
|
|
this->framelessEmbedWindow_->show();
|
|
|
|
}
|
|
|
|
|
2020-09-19 17:14:10 +02:00
|
|
|
// No main window has been created from loading, create an empty one
|
2021-01-23 16:26:42 +01:00
|
|
|
if (this->mainWindow_ == nullptr)
|
2018-10-21 13:43:02 +02:00
|
|
|
{
|
2021-01-23 16:26:42 +01:00
|
|
|
this->mainWindow_ = &this->createWindow(WindowType::Main);
|
|
|
|
this->mainWindow_->getNotebook().addPage(true);
|
2021-04-17 16:15:23 +02:00
|
|
|
|
|
|
|
// TODO: don't create main window if it's a frameless embed
|
|
|
|
if (getArgs().isFramelessEmbed)
|
|
|
|
{
|
|
|
|
this->mainWindow_->hide();
|
|
|
|
}
|
2018-04-06 23:31:34 +02:00
|
|
|
}
|
|
|
|
|
2020-11-08 12:02:19 +01:00
|
|
|
settings.timestampFormat.connect([this](auto, auto) {
|
|
|
|
this->layoutChannelViews();
|
|
|
|
});
|
|
|
|
|
|
|
|
settings.emoteScale.connect([this](auto, auto) {
|
|
|
|
this->forceLayoutChannelViews();
|
|
|
|
});
|
|
|
|
|
|
|
|
settings.timestampFormat.connect([this](auto, auto) {
|
|
|
|
this->forceLayoutChannelViews();
|
|
|
|
});
|
|
|
|
settings.alternateMessages.connect([this](auto, auto) {
|
|
|
|
this->forceLayoutChannelViews();
|
|
|
|
});
|
|
|
|
settings.separateMessages.connect([this](auto, auto) {
|
|
|
|
this->forceLayoutChannelViews();
|
|
|
|
});
|
|
|
|
settings.collpseMessagesMinLines.connect([this](auto, auto) {
|
|
|
|
this->forceLayoutChannelViews();
|
|
|
|
});
|
|
|
|
settings.enableRedeemedHighlight.connect([this](auto, auto) {
|
|
|
|
this->forceLayoutChannelViews();
|
|
|
|
});
|
2018-07-07 11:41:01 +02:00
|
|
|
|
2018-07-06 19:23:47 +02:00
|
|
|
this->initialized_ = true;
|
2018-04-06 23:31:34 +02:00
|
|
|
}
|
2017-01-28 22:35:23 +01:00
|
|
|
|
2018-04-06 23:31:34 +02:00
|
|
|
void WindowManager::save()
|
|
|
|
{
|
2020-09-26 16:03:51 +02:00
|
|
|
if (getArgs().dontSaveSettings)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2020-11-21 16:20:10 +01:00
|
|
|
qCDebug(chatterinoWindowmanager) << "[WindowManager] Saving";
|
2018-06-26 17:06:17 +02:00
|
|
|
assertInGuiThread();
|
2018-04-06 23:31:34 +02:00
|
|
|
QJsonDocument document;
|
2017-01-28 22:35:23 +01:00
|
|
|
|
2018-04-06 23:31:34 +02:00
|
|
|
// "serialize"
|
2021-08-21 14:16:00 +02:00
|
|
|
QJsonArray windowArr;
|
2018-10-21 13:43:02 +02:00
|
|
|
for (Window *window : this->windows_)
|
|
|
|
{
|
2021-08-21 14:16:00 +02:00
|
|
|
QJsonObject windowObj;
|
2018-04-06 23:31:34 +02:00
|
|
|
|
|
|
|
// window type
|
2018-10-21 13:43:02 +02:00
|
|
|
switch (window->getType())
|
|
|
|
{
|
2018-08-11 22:23:06 +02:00
|
|
|
case WindowType::Main:
|
2021-08-21 14:16:00 +02:00
|
|
|
windowObj.insert("type", "main");
|
2018-04-06 23:31:34 +02:00
|
|
|
break;
|
2018-07-07 11:41:01 +02:00
|
|
|
|
2018-08-11 22:23:06 +02:00
|
|
|
case WindowType::Popup:
|
2021-08-21 14:16:00 +02:00
|
|
|
windowObj.insert("type", "popup");
|
2018-04-06 23:31:34 +02:00
|
|
|
break;
|
2018-07-07 11:41:01 +02:00
|
|
|
|
2018-08-11 22:23:06 +02:00
|
|
|
case WindowType::Attached:;
|
2018-04-06 23:31:34 +02:00
|
|
|
}
|
|
|
|
|
2018-10-29 22:11:42 +01:00
|
|
|
if (window->isMaximized())
|
|
|
|
{
|
2021-08-21 14:16:00 +02:00
|
|
|
windowObj.insert("state", "maximized");
|
2018-10-29 22:11:42 +01:00
|
|
|
}
|
|
|
|
else if (window->isMinimized())
|
|
|
|
{
|
2021-08-21 14:16:00 +02:00
|
|
|
windowObj.insert("state", "minimized");
|
2018-10-29 22:11:42 +01:00
|
|
|
}
|
|
|
|
|
2018-04-06 23:31:34 +02:00
|
|
|
// window geometry
|
2019-09-01 14:06:30 +02:00
|
|
|
auto rect = window->getBounds();
|
|
|
|
|
2021-08-21 14:16:00 +02:00
|
|
|
windowObj.insert("x", rect.x());
|
|
|
|
windowObj.insert("y", rect.y());
|
|
|
|
windowObj.insert("width", rect.width());
|
|
|
|
windowObj.insert("height", rect.height());
|
2018-04-06 23:31:34 +02:00
|
|
|
|
2021-08-21 14:16:00 +02:00
|
|
|
QJsonObject emotePopupObj;
|
|
|
|
emotePopupObj.insert("x", this->emotePopupPos_.x());
|
|
|
|
emotePopupObj.insert("y", this->emotePopupPos_.y());
|
|
|
|
windowObj.insert("emotePopup", emotePopupObj);
|
2020-04-13 13:15:51 +02:00
|
|
|
|
2018-04-06 23:31:34 +02:00
|
|
|
// window tabs
|
2021-08-21 14:16:00 +02:00
|
|
|
QJsonArray tabsArr;
|
2018-04-06 23:31:34 +02:00
|
|
|
|
2021-08-21 14:16:00 +02:00
|
|
|
for (int tabIndex = 0; tabIndex < window->getNotebook().getPageCount();
|
|
|
|
tabIndex++)
|
2018-10-21 13:43:02 +02:00
|
|
|
{
|
2021-08-21 14:16:00 +02:00
|
|
|
QJsonObject tabObj;
|
2018-08-06 21:17:03 +02:00
|
|
|
SplitContainer *tab = dynamic_cast<SplitContainer *>(
|
2021-08-21 14:16:00 +02:00
|
|
|
window->getNotebook().getPageAt(tabIndex));
|
2018-05-23 04:22:17 +02:00
|
|
|
assert(tab != nullptr);
|
2018-04-06 23:31:34 +02:00
|
|
|
|
2021-08-21 14:16:00 +02:00
|
|
|
bool isSelected = window->getNotebook().getSelectedPage() == tab;
|
|
|
|
WindowManager::encodeTab(tab, isSelected, tabObj);
|
|
|
|
tabsArr.append(tabObj);
|
2018-04-06 23:31:34 +02:00
|
|
|
}
|
|
|
|
|
2021-08-21 14:16:00 +02:00
|
|
|
windowObj.insert("tabs", tabsArr);
|
|
|
|
windowArr.append(windowObj);
|
2018-04-06 23:31:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
QJsonObject obj;
|
2021-08-21 14:16:00 +02:00
|
|
|
obj.insert("windows", windowArr);
|
2018-04-06 23:31:34 +02:00
|
|
|
document.setObject(obj);
|
|
|
|
|
|
|
|
// save file
|
2020-09-19 17:14:10 +02:00
|
|
|
QSaveFile file(this->windowLayoutFilePath);
|
2018-04-06 23:31:34 +02:00
|
|
|
file.open(QIODevice::WriteOnly | QIODevice::Truncate);
|
2018-05-16 14:55:45 +02:00
|
|
|
|
|
|
|
QJsonDocument::JsonFormat format =
|
|
|
|
#ifdef _DEBUG
|
|
|
|
QJsonDocument::JsonFormat::Compact
|
|
|
|
#else
|
|
|
|
(QJsonDocument::JsonFormat)0
|
|
|
|
#endif
|
|
|
|
;
|
|
|
|
|
|
|
|
file.write(document.toJson(format));
|
2019-05-01 09:58:13 +02:00
|
|
|
file.commit();
|
2018-04-06 23:31:34 +02:00
|
|
|
}
|
|
|
|
|
2018-10-07 12:55:44 +02:00
|
|
|
void WindowManager::sendAlert()
|
|
|
|
{
|
|
|
|
int flashDuration = 2500;
|
2018-10-21 13:43:02 +02:00
|
|
|
if (getSettings()->longAlerts)
|
|
|
|
{
|
2018-10-07 12:55:44 +02:00
|
|
|
flashDuration = 0;
|
|
|
|
}
|
2018-10-07 15:37:17 +02:00
|
|
|
QApplication::alert(this->getMainWindow().window(), flashDuration);
|
2018-10-07 12:55:44 +02:00
|
|
|
}
|
|
|
|
|
2018-10-07 18:27:40 +02:00
|
|
|
void WindowManager::queueSave()
|
|
|
|
{
|
|
|
|
using namespace std::chrono_literals;
|
|
|
|
|
|
|
|
this->saveTimer->start(10s);
|
|
|
|
}
|
|
|
|
|
2021-08-21 14:16:00 +02:00
|
|
|
void WindowManager::encodeTab(SplitContainer *tab, bool isSelected,
|
|
|
|
QJsonObject &obj)
|
|
|
|
{
|
|
|
|
// custom tab title
|
|
|
|
if (tab->getTab()->hasCustomTitle())
|
|
|
|
{
|
|
|
|
obj.insert("title", tab->getTab()->getCustomTitle());
|
|
|
|
}
|
|
|
|
|
|
|
|
// selected
|
|
|
|
if (isSelected)
|
|
|
|
{
|
|
|
|
obj.insert("selected", true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// highlighting on new messages
|
|
|
|
obj.insert("highlightsEnabled", tab->getTab()->hasHighlightsEnabled());
|
|
|
|
|
|
|
|
// splits
|
|
|
|
QJsonObject splits;
|
|
|
|
|
|
|
|
WindowManager::encodeNodeRecursively(tab->getBaseNode(), splits);
|
|
|
|
|
|
|
|
obj.insert("splits2", splits);
|
|
|
|
}
|
|
|
|
|
2020-10-18 15:16:56 +02:00
|
|
|
void WindowManager::encodeNodeRecursively(SplitNode *node, QJsonObject &obj)
|
2018-05-16 14:55:45 +02:00
|
|
|
{
|
2018-10-21 13:43:02 +02:00
|
|
|
switch (node->getType())
|
|
|
|
{
|
2019-09-22 15:32:36 +02:00
|
|
|
case SplitNode::_Split: {
|
2018-05-16 14:55:45 +02:00
|
|
|
obj.insert("type", "split");
|
2019-03-24 15:38:09 +01:00
|
|
|
obj.insert("moderationMode", node->getSplit()->getModerationMode());
|
2020-10-18 15:16:56 +02:00
|
|
|
|
2018-05-16 14:55:45 +02:00
|
|
|
QJsonObject split;
|
2021-08-21 14:16:00 +02:00
|
|
|
WindowManager::encodeChannel(node->getSplit()->getIndirectChannel(),
|
|
|
|
split);
|
2018-05-16 14:55:45 +02:00
|
|
|
obj.insert("data", split);
|
2020-10-18 15:16:56 +02:00
|
|
|
|
|
|
|
QJsonArray filters;
|
2021-08-21 14:16:00 +02:00
|
|
|
WindowManager::encodeFilters(node->getSplit(), filters);
|
2020-10-18 15:16:56 +02:00
|
|
|
obj.insert("filters", filters);
|
2018-10-21 13:43:02 +02:00
|
|
|
}
|
|
|
|
break;
|
2018-05-16 14:55:45 +02:00
|
|
|
case SplitNode::HorizontalContainer:
|
2019-09-22 15:32:36 +02:00
|
|
|
case SplitNode::VerticalContainer: {
|
2018-08-06 21:17:03 +02:00
|
|
|
obj.insert("type", node->getType() == SplitNode::HorizontalContainer
|
|
|
|
? "horizontal"
|
|
|
|
: "vertical");
|
2018-05-16 14:55:45 +02:00
|
|
|
|
2021-08-21 14:16:00 +02:00
|
|
|
QJsonArray itemsArr;
|
2018-10-21 13:43:02 +02:00
|
|
|
for (const std::unique_ptr<SplitNode> &n : node->getChildren())
|
|
|
|
{
|
2018-05-16 14:55:45 +02:00
|
|
|
QJsonObject subObj;
|
2021-08-21 14:16:00 +02:00
|
|
|
WindowManager::encodeNodeRecursively(n.get(), subObj);
|
|
|
|
itemsArr.append(subObj);
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
2021-08-21 14:16:00 +02:00
|
|
|
obj.insert("items", itemsArr);
|
2018-10-21 13:43:02 +02:00
|
|
|
}
|
|
|
|
break;
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
2021-04-17 15:16:14 +02:00
|
|
|
|
|
|
|
obj.insert("flexh", node->getHorizontalFlex());
|
|
|
|
obj.insert("flexv", node->getVerticalFlex());
|
2018-05-16 14:55:45 +02:00
|
|
|
}
|
|
|
|
|
2018-04-20 22:33:28 +02:00
|
|
|
void WindowManager::encodeChannel(IndirectChannel channel, QJsonObject &obj)
|
|
|
|
{
|
2018-06-26 17:06:17 +02:00
|
|
|
assertInGuiThread();
|
2018-04-26 18:10:26 +02:00
|
|
|
|
2018-10-21 13:43:02 +02:00
|
|
|
switch (channel.getType())
|
|
|
|
{
|
2019-09-22 15:32:36 +02:00
|
|
|
case Channel::Type::Twitch: {
|
2018-04-20 22:33:28 +02:00
|
|
|
obj.insert("type", "twitch");
|
2018-08-02 14:23:27 +02:00
|
|
|
obj.insert("name", channel.get()->getName());
|
2018-10-21 13:43:02 +02:00
|
|
|
}
|
|
|
|
break;
|
2019-09-22 15:32:36 +02:00
|
|
|
case Channel::Type::TwitchMentions: {
|
2018-04-20 22:33:28 +02:00
|
|
|
obj.insert("type", "mentions");
|
2018-10-21 13:43:02 +02:00
|
|
|
}
|
|
|
|
break;
|
2019-09-22 15:32:36 +02:00
|
|
|
case Channel::Type::TwitchWatching: {
|
2018-04-20 22:33:28 +02:00
|
|
|
obj.insert("type", "watching");
|
2018-10-21 13:43:02 +02:00
|
|
|
}
|
|
|
|
break;
|
2019-09-22 15:32:36 +02:00
|
|
|
case Channel::Type::TwitchWhispers: {
|
2018-04-20 22:33:28 +02:00
|
|
|
obj.insert("type", "whispers");
|
2018-10-21 13:43:02 +02:00
|
|
|
}
|
|
|
|
break;
|
2021-05-09 16:17:04 +02:00
|
|
|
case Channel::Type::TwitchLive: {
|
|
|
|
obj.insert("type", "live");
|
|
|
|
}
|
|
|
|
break;
|
2019-09-22 15:32:36 +02:00
|
|
|
case Channel::Type::Irc: {
|
2019-09-11 00:10:49 +02:00
|
|
|
if (auto ircChannel =
|
|
|
|
dynamic_cast<IrcChannel *>(channel.get().get()))
|
|
|
|
{
|
|
|
|
obj.insert("type", "irc");
|
2019-09-14 18:38:09 +02:00
|
|
|
if (ircChannel->server())
|
|
|
|
{
|
|
|
|
obj.insert("server", ircChannel->server()->id());
|
|
|
|
}
|
2019-09-11 00:10:49 +02:00
|
|
|
obj.insert("channel", ircChannel->getName());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2018-04-20 22:33:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-18 15:16:56 +02:00
|
|
|
void WindowManager::encodeFilters(Split *split, QJsonArray &arr)
|
|
|
|
{
|
|
|
|
assertInGuiThread();
|
|
|
|
|
|
|
|
auto filters = split->getFilters();
|
|
|
|
for (const auto &f : filters)
|
|
|
|
{
|
|
|
|
arr.append(f.toString(QUuid::WithoutBraces));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-19 17:14:10 +02:00
|
|
|
IndirectChannel WindowManager::decodeChannel(const SplitDescriptor &descriptor)
|
2018-04-20 22:33:28 +02:00
|
|
|
{
|
2018-06-26 17:06:17 +02:00
|
|
|
assertInGuiThread();
|
2018-04-26 18:10:26 +02:00
|
|
|
|
2018-04-28 15:20:18 +02:00
|
|
|
auto app = getApp();
|
|
|
|
|
2020-09-19 17:14:10 +02:00
|
|
|
if (descriptor.type_ == "twitch")
|
2018-10-21 13:43:02 +02:00
|
|
|
{
|
2020-09-19 17:14:10 +02:00
|
|
|
return app->twitch.server->getOrAddChannel(descriptor.channelName_);
|
2018-10-21 13:43:02 +02:00
|
|
|
}
|
2020-09-19 17:14:10 +02:00
|
|
|
else if (descriptor.type_ == "mentions")
|
2018-10-21 13:43:02 +02:00
|
|
|
{
|
2018-04-28 15:20:18 +02:00
|
|
|
return app->twitch.server->mentionsChannel;
|
2018-10-21 13:43:02 +02:00
|
|
|
}
|
2020-09-19 17:14:10 +02:00
|
|
|
else if (descriptor.type_ == "watching")
|
2018-10-21 13:43:02 +02:00
|
|
|
{
|
2018-04-28 15:20:18 +02:00
|
|
|
return app->twitch.server->watchingChannel;
|
2018-10-21 13:43:02 +02:00
|
|
|
}
|
2020-09-19 17:14:10 +02:00
|
|
|
else if (descriptor.type_ == "whispers")
|
2018-10-21 13:43:02 +02:00
|
|
|
{
|
2018-04-28 15:20:18 +02:00
|
|
|
return app->twitch.server->whispersChannel;
|
2018-04-20 22:33:28 +02:00
|
|
|
}
|
2021-05-09 16:17:04 +02:00
|
|
|
else if (descriptor.type_ == "live")
|
|
|
|
{
|
|
|
|
return app->twitch.server->liveChannel;
|
|
|
|
}
|
2020-09-19 17:14:10 +02:00
|
|
|
else if (descriptor.type_ == "irc")
|
2019-09-11 00:10:49 +02:00
|
|
|
{
|
2020-09-19 17:14:10 +02:00
|
|
|
return Irc::instance().getOrAddChannel(descriptor.server_,
|
|
|
|
descriptor.channelName_);
|
2019-09-11 00:10:49 +02:00
|
|
|
}
|
2018-04-20 22:33:28 +02:00
|
|
|
|
|
|
|
return Channel::getEmpty();
|
|
|
|
}
|
|
|
|
|
2018-04-06 23:31:34 +02:00
|
|
|
void WindowManager::closeAll()
|
|
|
|
{
|
2018-06-26 17:06:17 +02:00
|
|
|
assertInGuiThread();
|
2018-04-26 18:10:26 +02:00
|
|
|
|
2018-10-21 13:43:02 +02:00
|
|
|
for (Window *window : windows_)
|
|
|
|
{
|
2018-04-06 23:31:34 +02:00
|
|
|
window->close();
|
2017-01-28 22:35:23 +01:00
|
|
|
}
|
2017-01-26 21:04:01 +01:00
|
|
|
}
|
2017-01-28 22:35:23 +01:00
|
|
|
|
2018-06-04 16:10:54 +02:00
|
|
|
int WindowManager::getGeneration() const
|
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
return this->generation_;
|
2018-06-04 16:10:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void WindowManager::incGeneration()
|
|
|
|
{
|
2018-07-06 19:23:47 +02:00
|
|
|
this->generation_++;
|
2018-06-04 16:10:54 +02:00
|
|
|
}
|
|
|
|
|
2020-09-19 17:14:10 +02:00
|
|
|
WindowLayout WindowManager::loadWindowLayoutFromFile() const
|
|
|
|
{
|
|
|
|
return WindowLayout::loadFromFile(this->windowLayoutFilePath);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WindowManager::applyWindowLayout(const WindowLayout &layout)
|
|
|
|
{
|
2021-04-17 16:15:23 +02:00
|
|
|
if (getArgs().dontLoadMainWindow)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-09-19 17:14:10 +02:00
|
|
|
// Set emote popup position
|
|
|
|
this->emotePopupPos_ = layout.emotePopupPos_;
|
|
|
|
|
|
|
|
for (const auto &windowData : layout.windows_)
|
|
|
|
{
|
|
|
|
auto type = windowData.type_;
|
|
|
|
|
|
|
|
Window &window = this->createWindow(type, false);
|
|
|
|
|
|
|
|
if (type == WindowType::Main)
|
|
|
|
{
|
|
|
|
assert(this->mainWindow_ == nullptr);
|
|
|
|
|
|
|
|
this->mainWindow_ = &window;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get geometry
|
|
|
|
{
|
|
|
|
// out of bounds windows
|
|
|
|
auto screens = qApp->screens();
|
2021-10-03 11:43:23 +02:00
|
|
|
bool outOfBounds =
|
|
|
|
!getenv("I3SOCK") &&
|
|
|
|
std::none_of(screens.begin(), screens.end(),
|
|
|
|
[&](QScreen *screen) {
|
|
|
|
return screen->availableGeometry().intersects(
|
|
|
|
windowData.geometry_);
|
|
|
|
});
|
2020-09-19 17:14:10 +02:00
|
|
|
|
|
|
|
// ask if move into bounds
|
|
|
|
auto &&should = shouldMoveOutOfBoundsWindow();
|
|
|
|
if (outOfBounds && !should)
|
|
|
|
{
|
|
|
|
should =
|
|
|
|
QMessageBox(QMessageBox::Icon::Warning,
|
|
|
|
"Windows out of bounds",
|
|
|
|
"Some windows were detected out of bounds. "
|
|
|
|
"Should they be moved into bounds?",
|
|
|
|
QMessageBox::Yes | QMessageBox::No)
|
|
|
|
.exec() == QMessageBox::Yes;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((!outOfBounds || !should.value()) &&
|
|
|
|
windowData.geometry_.x() != -1 &&
|
|
|
|
windowData.geometry_.y() != -1 &&
|
|
|
|
windowData.geometry_.width() != -1 &&
|
|
|
|
windowData.geometry_.height() != -1)
|
|
|
|
{
|
|
|
|
// Have to offset x by one because qt moves the window 1px too
|
|
|
|
// far to the left:w
|
|
|
|
|
|
|
|
window.setInitialBounds({windowData.geometry_.x(),
|
|
|
|
windowData.geometry_.y(),
|
|
|
|
windowData.geometry_.width(),
|
|
|
|
windowData.geometry_.height()});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// open tabs
|
|
|
|
for (const auto &tab : windowData.tabs_)
|
|
|
|
{
|
|
|
|
SplitContainer *page = window.getNotebook().addPage(false);
|
|
|
|
|
|
|
|
// set custom title
|
|
|
|
if (!tab.customTitle_.isEmpty())
|
|
|
|
{
|
|
|
|
page->getTab()->setCustomTitle(tab.customTitle_);
|
|
|
|
}
|
|
|
|
|
|
|
|
// selected
|
|
|
|
if (tab.selected_)
|
|
|
|
{
|
|
|
|
window.getNotebook().select(page);
|
|
|
|
}
|
|
|
|
|
|
|
|
// highlighting on new messages
|
|
|
|
page->getTab()->setHighlightsEnabled(tab.highlightsEnabled_);
|
|
|
|
|
|
|
|
if (tab.rootNode_)
|
|
|
|
{
|
|
|
|
page->applyFromDescriptor(*tab.rootNode_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
window.show();
|
|
|
|
|
|
|
|
// Set window state
|
|
|
|
switch (windowData.state_)
|
|
|
|
{
|
|
|
|
case WindowDescriptor::State::Minimized: {
|
|
|
|
window.setWindowState(Qt::WindowMinimized);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WindowDescriptor::State::Maximized: {
|
|
|
|
window.setWindowState(Qt::WindowMaximized);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-14 17:52:22 +02:00
|
|
|
} // namespace chatterino
|