rewrote window saveing/serialization system

fixes #212
This commit is contained in:
fourtf 2018-04-06 23:31:34 +02:00
parent 4ec2c0d8b3
commit cb06579c29
19 changed files with 291 additions and 316 deletions

View file

@ -21,10 +21,10 @@ Application::Application()
singletons::LoggingManager::getInstance(); singletons::LoggingManager::getInstance();
singletons::SettingManager::getInstance().init(); singletons::SettingManager::getInstance().initialize();
singletons::CommandManager::getInstance().loadCommands(); singletons::CommandManager::getInstance().loadCommands();
singletons::WindowManager::getInstance().initMainWindow(); singletons::WindowManager::getInstance().initialize();
// Initialize everything we need // Initialize everything we need
singletons::EmoteManager::getInstance().loadGlobalEmotes(); singletons::EmoteManager::getInstance().loadGlobalEmotes();

View file

@ -47,7 +47,7 @@ bool SettingManager::isIgnoredEmote(const QString &)
return false; return false;
} }
void SettingManager::init() void SettingManager::initialize()
{ {
QString settingsPath = PathManager::getInstance().settingsFolderPath + "/settings.json"; QString settingsPath = PathManager::getInstance().settingsFolderPath + "/settings.json";

View file

@ -27,7 +27,7 @@ public:
messages::MessageElement::Flags getWordFlags(); messages::MessageElement::Flags getWordFlags();
bool isIgnoredEmote(const QString &emote); bool isIgnoredEmote(const QString &emote);
void init(); void initialize();
/// Appearance /// Appearance
BoolSetting showTimestamps = {"/appearance/messages/showTimestamps", true}; BoolSetting showTimestamps = {"/appearance/messages/showTimestamps", true};

View file

@ -129,8 +129,8 @@ void ThemeManager::actuallyUpdate(double hue, double multiplier)
this->messages.textColors.system = QColor(140, 127, 127); this->messages.textColors.system = QColor(140, 127, 127);
this->messages.backgrounds.regular = splits.background; this->messages.backgrounds.regular = splits.background;
this->messages.backgrounds.highlighted = blendColors( this->messages.backgrounds.highlighted =
this->tabs.selected.backgrounds.regular.color(), this->messages.backgrounds.regular, 0.8); blendColors(themeColor, this->messages.backgrounds.regular, 0.8);
// this->messages.backgrounds.resub // this->messages.backgrounds.resub
// this->messages.backgrounds.whisper // this->messages.backgrounds.whisper
this->messages.disabled = getColor(0, sat, 1, 0.6); this->messages.disabled = getColor(0, sat, 1, 0.6);

View file

@ -1,12 +1,19 @@
#include "windowmanager.hpp" #include "windowmanager.hpp"
#include "debug/log.hpp" #include "debug/log.hpp"
#include "providers/twitch/twitchserver.hpp"
#include "singletons/fontmanager.hpp" #include "singletons/fontmanager.hpp"
#include "singletons/pathmanager.hpp"
#include "singletons/thememanager.hpp" #include "singletons/thememanager.hpp"
#include "widgets/accountswitchpopupwidget.hpp" #include "widgets/accountswitchpopupwidget.hpp"
#include "widgets/settingsdialog.hpp" #include "widgets/settingsdialog.hpp"
#include <QJsonDocument>
#include <QJsonObject>
#include <QDebug> #include <QDebug>
#define SETTINGS_FILENAME "/layout.json"
namespace chatterino { namespace chatterino {
namespace singletons { namespace singletons {
@ -51,11 +58,6 @@ WindowManager::WindowManager(ThemeManager &_themeManager)
_themeManager.repaintVisibleChatWidgets.connect([this] { this->repaintVisibleChatWidgets(); }); _themeManager.repaintVisibleChatWidgets.connect([this] { this->repaintVisibleChatWidgets(); });
} }
void WindowManager::initMainWindow()
{
this->selectedWindow = this->mainWindow = new widgets::Window("main", this->themeManager, true);
}
void WindowManager::layoutVisibleChatWidgets(Channel *channel) void WindowManager::layoutVisibleChatWidgets(Channel *channel)
{ {
this->layout.invoke(channel); this->layout.invoke(channel);
@ -90,12 +92,12 @@ widgets::Window &WindowManager::getSelectedWindow()
return *this->selectedWindow; return *this->selectedWindow;
} }
widgets::Window &WindowManager::createWindow() widgets::Window &WindowManager::createWindow(widgets::Window::WindowType type)
{ {
auto *window = new widgets::Window("external", this->themeManager, false); auto *window = new widgets::Window(this->themeManager, type);
window->getNotebook().addNewPage();
this->windows.push_back(window); this->windows.push_back(window);
window->show();
return *window; return *window;
} }
@ -115,14 +117,168 @@ widgets::Window *WindowManager::windowAt(int index)
return this->windows.at(index); return this->windows.at(index);
} }
void WindowManager::initialize()
{
assert(!this->initialized);
// load file
QString settingsPath = PathManager::getInstance().settingsFolderPath + SETTINGS_FILENAME;
QFile file(settingsPath);
file.open(QIODevice::ReadOnly);
QByteArray data = file.readAll();
QJsonDocument document = QJsonDocument::fromJson(data);
QJsonArray windows_arr = document.object().value("windows").toArray();
// "deserialize"
for (QJsonValue window_val : windows_arr) {
QJsonObject window_obj = window_val.toObject();
// get type
QString type_val = window_obj.value("type").toString();
widgets::Window::WindowType type =
type_val == "main" ? widgets::Window::Main : widgets::Window::Popup;
if (type == widgets::Window::Main && mainWindow != nullptr) {
type = widgets::Window::Popup;
}
widgets::Window &window = createWindow(type);
if (type == widgets::Window::Main) {
mainWindow = &window;
}
// get geometry
{
int x = window_obj.value("x").toInt(-1);
int y = window_obj.value("y").toInt(-1);
int width = window_obj.value("width").toInt(-1);
int height = window_obj.value("height").toInt(-1);
if (x != -1 && y != -1 && width != -1 && height != -1) {
window.setGeometry(x, y, width, height);
}
}
// load tabs
QJsonArray tabs = window_obj.value("tabs").toArray();
for (QJsonValue tab_val : tabs) {
widgets::SplitContainer *tab = window.getNotebook().addNewPage();
QJsonObject tab_obj = tab_val.toObject();
// set custom title
QJsonValue title_val = tab_obj.value("title");
if (title_val.isString()) {
tab->getTab()->setTitle(title_val.toString());
tab->getTab()->useDefaultTitle = false;
}
// load splits
int colNr = 0;
for (QJsonValue column_val : tab_obj.value("splits").toArray()) {
for (QJsonValue split_val : column_val.toArray()) {
widgets::Split *split = new widgets::Split(tab);
QJsonObject split_obj = split_val.toObject();
QJsonValue channelName_val = split_obj.value("channelName");
if (channelName_val.isString()) {
split->setChannel(providers::twitch::TwitchServer::getInstance().addChannel(
channelName_val.toString()));
}
tab->addToLayout(split, std::make_pair(colNr, -1));
}
colNr++;
}
}
}
if (mainWindow == nullptr) {
mainWindow = &createWindow(widgets::Window::Main);
mainWindow->getNotebook().addNewPage(true);
}
this->initialized = true;
}
void WindowManager::save() void WindowManager::save()
{ {
assert(this->mainWindow); QJsonDocument document;
this->mainWindow->save();
// "serialize"
QJsonArray window_arr;
for (widgets::Window *window : this->windows) { for (widgets::Window *window : this->windows) {
window->save(); QJsonObject window_obj;
// window type
switch (window->getType()) {
case widgets::Window::Main:
window_obj.insert("type", "main");
break;
case widgets::Window::Popup:
window_obj.insert("type", "popup");
break;
}
// window geometry
window_obj.insert("x", window->x());
window_obj.insert("y", window->y());
window_obj.insert("width", window->width());
window_obj.insert("height", window->height());
// window tabs
QJsonArray tabs_arr;
for (int tab_i = 0; tab_i < window->getNotebook().tabCount(); tab_i++) {
QJsonObject tab_obj;
widgets::SplitContainer *tab = window->getNotebook().tabAt(tab_i);
// custom tab title
if (!tab->getTab()->useDefaultTitle) {
tab_obj.insert("title", tab->getTab()->getTitle());
}
// splits
QJsonArray columns_arr;
std::vector<std::vector<widgets::Split *>> columns = tab->getColumns();
for (std::vector<widgets::Split *> &cells : columns) {
QJsonArray cells_arr;
for (widgets::Split *cell : cells) {
QJsonObject cell_obj;
cell_obj.insert("channelName", cell->getChannel()->name);
cells_arr.append(cell_obj);
}
columns_arr.append(cells_arr);
}
tab_obj.insert("splits", columns_arr);
tabs_arr.append(tab_obj);
}
window_obj.insert("tabs", tabs_arr);
window_arr.append(window_obj);
}
QJsonObject obj;
obj.insert("windows", window_arr);
document.setObject(obj);
// save file
QString settingsPath = PathManager::getInstance().settingsFolderPath + SETTINGS_FILENAME;
QFile file(settingsPath);
file.open(QIODevice::WriteOnly | QIODevice::Truncate);
file.write(document.toJson());
file.flush();
}
void WindowManager::closeAll()
{
for (widgets::Window *window : windows) {
window->close();
} }
} }

View file

@ -17,7 +17,6 @@ public:
void showSettingsDialog(); void showSettingsDialog();
void showAccountSelectPopup(QPoint point); void showAccountSelectPopup(QPoint point);
void initMainWindow();
void layoutVisibleChatWidgets(Channel *channel = nullptr); void layoutVisibleChatWidgets(Channel *channel = nullptr);
void repaintVisibleChatWidgets(Channel *channel = nullptr); void repaintVisibleChatWidgets(Channel *channel = nullptr);
void repaintGifEmotes(); void repaintGifEmotes();
@ -25,12 +24,14 @@ public:
widgets::Window &getMainWindow(); widgets::Window &getMainWindow();
widgets::Window &getSelectedWindow(); widgets::Window &getSelectedWindow();
widgets::Window &createWindow(); widgets::Window &createWindow(widgets::Window::WindowType type);
int windowCount(); int windowCount();
widgets::Window *windowAt(int index); widgets::Window *windowAt(int index);
void save(); void save();
void initialize();
void closeAll();
pajlada::Signals::NoArgSignal repaintGifs; pajlada::Signals::NoArgSignal repaintGifs;
pajlada::Signals::Signal<Channel *> layout; pajlada::Signals::Signal<Channel *> layout;
@ -38,6 +39,8 @@ public:
private: private:
ThemeManager &themeManager; ThemeManager &themeManager;
bool initialized = false;
std::vector<widgets::Window *> windows; std::vector<widgets::Window *> windows;
widgets::Window *mainWindow = nullptr; widgets::Window *mainWindow = nullptr;

View file

@ -69,7 +69,12 @@ void BaseWindow::init()
layout->addLayout(buttonLayout); layout->addLayout(buttonLayout);
// title // title
QLabel *title = new QLabel(" Chatterino"); // QLabel *title = new QLabel(" Chatterino");
QLabel *title = new QLabel("");
QSizePolicy policy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
policy.setHorizontalStretch(1);
title->setBaseSize(0, 0);
title->setSizePolicy(policy);
buttonLayout->addWidget(title); buttonLayout->addWidget(title);
this->titleLabel = title; this->titleLabel = title;

View file

@ -111,6 +111,7 @@ ChannelView::ChannelView(BaseWidget *parent)
QObject::connect(this->layoutCooldown, &QTimer::timeout, [this] { QObject::connect(this->layoutCooldown, &QTimer::timeout, [this] {
if (this->layoutQueued) { if (this->layoutQueued) {
this->layoutMessages(); this->layoutMessages();
this->layoutQueued = false;
} }
}); });
} }
@ -137,15 +138,15 @@ void ChannelView::themeRefreshEvent()
void ChannelView::queueUpdate() void ChannelView::queueUpdate()
{ {
if (this->updateTimer.isActive()) { // if (this->updateTimer.isActive()) {
this->updateQueued = true; // this->updateQueued = true;
return; // return;
} // }
// this->repaint(); // this->repaint();
this->update(); this->update();
this->updateTimer.start(); // this->updateTimer.start();
} }
void ChannelView::layoutMessages() void ChannelView::layoutMessages()

View file

@ -17,14 +17,10 @@
namespace chatterino { namespace chatterino {
namespace widgets { namespace widgets {
NotebookTab::NotebookTab(Notebook *_notebook, const std::string &_uuid) NotebookTab::NotebookTab(Notebook *_notebook)
: BaseWidget(_notebook) : BaseWidget(_notebook)
, uuid(_uuid)
, settingRoot(fS("/containers/{}/tab", this->uuid))
, positionChangedAnimation(this, "pos") , positionChangedAnimation(this, "pos")
, notebook(_notebook) , notebook(_notebook)
, title(fS("{}/title", this->settingRoot), "")
, useDefaultBehaviour(fS("{}/useDefaultBehaviour", this->settingRoot), true)
, menu(this) , menu(this)
{ {
this->setAcceptDrops(true); this->setAcceptDrops(true);
@ -40,7 +36,7 @@ NotebookTab::NotebookTab(Notebook *_notebook, const std::string &_uuid)
TextInputDialog d(this); TextInputDialog d(this);
d.setWindowTitle("Change tab title (Leave empty for default behaviour)"); d.setWindowTitle("Change tab title (Leave empty for default behaviour)");
if (this->useDefaultBehaviour) { if (this->useDefaultTitle) {
d.setText(""); d.setText("");
} else { } else {
d.setText(this->getTitle()); d.setText(this->getTitle());
@ -49,10 +45,10 @@ NotebookTab::NotebookTab(Notebook *_notebook, const std::string &_uuid)
if (d.exec() == QDialog::Accepted) { if (d.exec() == QDialog::Accepted) {
QString newTitle = d.getText(); QString newTitle = d.getText();
if (newTitle.isEmpty()) { if (newTitle.isEmpty()) {
this->useDefaultBehaviour = true; this->useDefaultTitle = true;
this->page->refreshTitle(); this->page->refreshTitle();
} else { } else {
this->useDefaultBehaviour = false; this->useDefaultTitle = false;
this->setTitle(newTitle); this->setTitle(newTitle);
} }
} }
@ -82,11 +78,10 @@ void NotebookTab::updateSize()
int width; int width;
QString qTitle(qS(this->title));
if (singletons::SettingManager::getInstance().hideTabX) { if (singletons::SettingManager::getInstance().hideTabX) {
width = (int)((fontMetrics().width(qTitle) + 16 /*+ 16*/) * scale); width = (int)((fontMetrics().width(this->title) + 16 /*+ 16*/) * scale);
} else { } else {
width = (int)((fontMetrics().width(qTitle) + 8 + 24 /*+ 16*/) * scale); width = (int)((fontMetrics().width(this->title) + 8 + 24 /*+ 16*/) * scale);
} }
this->resize(std::min((int)(150 * scale), width), (int)(24 * scale)); this->resize(std::min((int)(150 * scale), width), (int)(24 * scale));
@ -96,17 +91,15 @@ void NotebookTab::updateSize()
} }
} }
QString NotebookTab::getTitle() const const QString &NotebookTab::getTitle() const
{ {
return qS(this->title); return this->title;
} }
void NotebookTab::setTitle(const QString &newTitle) void NotebookTab::setTitle(const QString &newTitle)
{ {
auto stdTitle = newTitle.toStdString(); if (this->title != newTitle) {
this->title = newTitle;
if (this->title != stdTitle) {
this->title = stdTitle;
this->updateSize(); this->updateSize();
this->update(); this->update();
} }

View file

@ -18,17 +18,14 @@ class NotebookTab : public BaseWidget
{ {
Q_OBJECT Q_OBJECT
const std::string uuid;
const std::string settingRoot;
public: public:
explicit NotebookTab(Notebook *_notebook, const std::string &_uuid); explicit NotebookTab(Notebook *_notebook);
void updateSize(); void updateSize();
SplitContainer *page; SplitContainer *page;
QString getTitle() const; const QString &getTitle() const;
void setTitle(const QString &newTitle); void setTitle(const QString &newTitle);
bool isSelected() const; bool isSelected() const;
void setSelected(bool value); void setSelected(bool value);
@ -63,10 +60,10 @@ private:
Notebook *notebook; Notebook *notebook;
pajlada::Settings::Setting<std::string> title; QString title;
public: public:
pajlada::Settings::Setting<bool> useDefaultBehaviour; bool useDefaultTitle = true;
private: private:
bool selected = false; bool selected = false;

View file

@ -155,7 +155,7 @@ void SplitHeader::scaleChangedEvent(float scale)
void SplitHeader::updateChannelText() void SplitHeader::updateChannelText()
{ {
const QString channelName = this->split->channelName; const QString channelName = this->split->getChannel()->name;
if (channelName.isEmpty()) { if (channelName.isEmpty()) {
this->titleLabel->setText("<no channel>"); this->titleLabel->setText("<no channel>");
return; return;

View file

@ -22,15 +22,13 @@
namespace chatterino { namespace chatterino {
namespace widgets { namespace widgets {
Notebook::Notebook(Window *parent, bool _showButtons, const std::string &settingPrefix) Notebook::Notebook(Window *parent, bool _showButtons)
: BaseWidget(parent) : BaseWidget(parent)
, settingRoot(fS("{}/notebook", settingPrefix))
, parentWindow(parent) , parentWindow(parent)
, addButton(this) , addButton(this)
, settingsButton(this) , settingsButton(this)
, userButton(this) , userButton(this)
, showButtons(_showButtons) , showButtons(_showButtons)
, tabs(fS("{}/tabs", this->settingRoot))
, closeConfirmDialog(this) , closeConfirmDialog(this)
{ {
this->connect(&this->settingsButton, SIGNAL(clicked()), this, SLOT(settingsButtonClicked())); this->connect(&this->settingsButton, SIGNAL(clicked()), this, SLOT(settingsButtonClicked()));
@ -47,8 +45,6 @@ Notebook::Notebook(Window *parent, bool _showButtons, const std::string &setting
settingsManager.hidePreferencesButton.connectSimple([this](auto) { this->performLayout(); }); settingsManager.hidePreferencesButton.connectSimple([this](auto) { this->performLayout(); });
settingsManager.hideUserButton.connectSimple([this](auto) { this->performLayout(); }); settingsManager.hideUserButton.connectSimple([this](auto) { this->performLayout(); });
this->loadTabs();
closeConfirmDialog.setText("Are you sure you want to close this tab?"); closeConfirmDialog.setText("Are you sure you want to close this tab?");
closeConfirmDialog.setIcon(QMessageBox::Icon::Question); closeConfirmDialog.setIcon(QMessageBox::Icon::Question);
closeConfirmDialog.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); closeConfirmDialog.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
@ -57,15 +53,10 @@ Notebook::Notebook(Window *parent, bool _showButtons, const std::string &setting
this->scaleChangedEvent(this->getScale()); this->scaleChangedEvent(this->getScale());
} }
SplitContainer *Notebook::addNewPage() SplitContainer *Notebook::addNewPage(bool select)
{ {
return this->addPage(CreateUUID().toStdString(), true); auto tab = new NotebookTab(this);
} auto page = new SplitContainer(this, tab);
SplitContainer *Notebook::addPage(const std::string &uuid, bool select)
{
auto tab = new NotebookTab(this, uuid);
auto page = new SplitContainer(this, tab, uuid);
tab->show(); tab->show();
@ -178,6 +169,11 @@ SplitContainer *Notebook::tabAt(QPoint point, int &index, int maxWidth)
return nullptr; return nullptr;
} }
SplitContainer *Notebook::tabAt(int index)
{
return this->pages[index];
}
void Notebook::rearrangePage(SplitContainer *page, int index) void Notebook::rearrangePage(SplitContainer *page, int index)
{ {
this->pages.move(this->pages.indexOf(page), index); this->pages.move(this->pages.indexOf(page), index);
@ -244,7 +240,7 @@ void Notebook::performLayout(bool animated)
for (auto &i : this->pages) { for (auto &i : this->pages) {
if (!first && if (!first &&
(i == this->pages.last() ? tabHeight : 0) + x + i->getTab()->width() > width()) { (i == this->pages.last() ? tabHeight : 0) + x + i->getTab()->width() > width()) {
y += i->getTab()->height() - 1; y += i->getTab()->height();
// y += 20; // y += 20;
i->getTab()->moveAnimated(QPoint(0, y), animated); i->getTab()->moveAnimated(QPoint(0, y), animated);
x = i->getTab()->width(); x = i->getTab()->width();
@ -310,33 +306,7 @@ void Notebook::usersButtonClicked()
void Notebook::addPageButtonClicked() void Notebook::addPageButtonClicked()
{ {
QTimer::singleShot(80, [this] { this->addNewPage(); }); QTimer::singleShot(80, [this] { this->addNewPage(true); });
}
void Notebook::loadTabs()
{
const std::vector<std::string> tabArray = this->tabs.getValue();
if (tabArray.size() == 0) {
this->addNewPage();
return;
}
for (const std::string &tabUUID : tabArray) {
this->addPage(tabUUID);
}
}
void Notebook::save()
{
std::vector<std::string> tabArray;
for (const auto &page : this->pages) {
tabArray.push_back(page->getUUID());
page->save();
}
this->tabs = tabArray;
} }
} // namespace widgets } // namespace widgets

View file

@ -18,23 +18,24 @@ class Notebook : public BaseWidget
{ {
Q_OBJECT Q_OBJECT
std::string settingRoot;
public: public:
enum HighlightType { none, highlighted, newMessage }; enum HighlightType { none, highlighted, newMessage };
explicit Notebook(Window *parent, bool _showButtons, const std::string &settingPrefix); explicit Notebook(Window *parent, bool _showButtons);
SplitContainer *addNewPage(); SplitContainer *addNewPage(bool select = false);
SplitContainer *addPage(const std::string &uuid, bool select = false);
void removePage(SplitContainer *page); void removePage(SplitContainer *page);
void removeCurrentPage(); void removeCurrentPage();
void select(SplitContainer *page); void select(SplitContainer *page);
void selectIndex(int index); void selectIndex(int index);
SplitContainer *getSelectedPage() const SplitContainer *getOrAddSelectedPage()
{ {
if (selectedPage == nullptr) {
this->addNewPage(true);
}
return selectedPage; return selectedPage;
} }
@ -42,6 +43,7 @@ public:
int tabCount(); int tabCount();
SplitContainer *tabAt(QPoint point, int &index, int maxWidth = 2000000000); SplitContainer *tabAt(QPoint point, int &index, int maxWidth = 2000000000);
SplitContainer *tabAt(int index);
void rearrangePage(SplitContainer *page, int index); void rearrangePage(SplitContainer *page, int index);
void nextTab(); void nextTab();
@ -71,14 +73,7 @@ private:
bool showButtons; bool showButtons;
pajlada::Settings::Setting<std::vector<std::string>> tabs;
void loadTabs();
QMessageBox closeConfirmDialog; QMessageBox closeConfirmDialog;
public:
void save();
}; };
} // namespace widgets } // namespace widgets

View file

@ -36,11 +36,8 @@ using namespace chatterino::messages;
namespace chatterino { namespace chatterino {
namespace widgets { namespace widgets {
Split::Split(SplitContainer *parent, const std::string &_uuid) Split::Split(SplitContainer *parent)
: BaseWidget(parent) : BaseWidget(parent)
, uuid(_uuid)
, settingRoot(fS("/splits/{}", this->uuid))
, channelName(fS("{}/channelName", this->settingRoot))
, parentPage(*parent) , parentPage(*parent)
, channel(Channel::getEmpty()) , channel(Channel::getEmpty())
, vbox(this) , vbox(this)
@ -86,11 +83,6 @@ Split::Split(SplitContainer *parent, const std::string &_uuid)
// CreateShortcut(this, "ALT+SHIFT+UP", &Split::doIncFlexY); // CreateShortcut(this, "ALT+SHIFT+UP", &Split::doIncFlexY);
// CreateShortcut(this, "ALT+SHIFT+DOWN", &Split::doDecFlexY); // CreateShortcut(this, "ALT+SHIFT+DOWN", &Split::doDecFlexY);
this->channelName.getValueChangedSignal().connect(
std::bind(&Split::channelNameUpdated, this, std::placeholders::_1));
this->channelNameUpdated(this->channelName.getValue());
this->input.ui.textEdit->installEventFilter(parent); this->input.ui.textEdit->installEventFilter(parent);
this->view.mouseDown.connect([this](QMouseEvent *) { this->giveFocus(Qt::MouseFocusReason); }); this->view.mouseDown.connect([this](QMouseEvent *) { this->giveFocus(Qt::MouseFocusReason); });
@ -130,11 +122,6 @@ Split::~Split()
this->channelIDChangedConnection.disconnect(); this->channelIDChangedConnection.disconnect();
} }
const std::string &Split::getUUID() const
{
return this->uuid;
}
ChannelPtr Split::getChannel() const ChannelPtr Split::getChannel() const
{ {
return this->channel; return this->channel;
@ -156,6 +143,7 @@ void Split::setChannel(ChannelPtr _newChannel)
} }
this->header.updateModerationModeIcon(); this->header.updateModerationModeIcon();
this->header.updateChannelText();
this->channelChanged.invoke(); this->channelChanged.invoke();
} }
@ -196,19 +184,6 @@ bool Split::getModerationMode() const
return this->moderationMode; return this->moderationMode;
} }
void Split::channelNameUpdated(const QString &newChannelName)
{
// update messages
if (newChannelName.isEmpty()) {
this->setChannel(Channel::getEmpty());
} else {
this->setChannel(TwitchServer::getInstance().addChannel(newChannelName));
}
// update header
this->header.updateChannelText();
}
bool Split::showChangeChannelPopup(const char *dialogTitle, bool empty) bool Split::showChangeChannelPopup(const char *dialogTitle, bool empty)
{ {
// create new input dialog and execute it // create new input dialog and execute it
@ -217,13 +192,13 @@ bool Split::showChangeChannelPopup(const char *dialogTitle, bool empty)
dialog.setWindowTitle(dialogTitle); dialog.setWindowTitle(dialogTitle);
if (!empty) { if (!empty) {
dialog.setText(this->channelName); dialog.setText(this->channel->name);
} }
if (dialog.exec() == QDialog::Accepted) { if (dialog.exec() == QDialog::Accepted) {
QString newChannelName = dialog.getText().trimmed(); QString newChannelName = dialog.getText().trimmed();
this->channelName = newChannelName; this->setChannel(providers::twitch::TwitchServer::getInstance().addChannel(newChannelName));
this->parentPage.refreshTitle(); this->parentPage.refreshTitle();
return true; return true;
@ -328,12 +303,13 @@ void Split::doChangeChannel()
void Split::doPopup() void Split::doPopup()
{ {
Window &window = singletons::WindowManager::getInstance().createWindow(); Window &window = singletons::WindowManager::getInstance().createWindow(Window::Popup);
Split *split = new Split(static_cast<SplitContainer *>(window.getNotebook().getSelectedPage()), Split *split =
this->uuid); new Split(static_cast<SplitContainer *>(window.getNotebook().getOrAddSelectedPage()));
window.getNotebook().getSelectedPage()->addToLayout(split); split->setChannel(this->getChannel());
window.getNotebook().getOrAddSelectedPage()->addToLayout(split);
window.show(); window.show();
} }
@ -366,7 +342,7 @@ void Split::doOpenPopupPlayer()
void Split::doOpenStreamlink() void Split::doOpenStreamlink()
{ {
try { try {
streamlink::Start(this->channelName.getValue()); streamlink::Start(this->channel->name);
} catch (const streamlink::Exception &ex) { } catch (const streamlink::Exception &ex) {
debug::Log("Error in doOpenStreamlink: {}", ex.what()); debug::Log("Error in doOpenStreamlink: {}", ex.what());
} }

View file

@ -42,14 +42,10 @@ class Split : public BaseWidget
Q_OBJECT Q_OBJECT
const std::string uuid;
const std::string settingRoot;
public: public:
Split(SplitContainer *parent, const std::string &_uuid); Split(SplitContainer *parent);
~Split() override; ~Split() override;
pajlada::Settings::Setting<QString> channelName;
pajlada::Signals::NoArgSignal channelChanged; pajlada::Signals::NoArgSignal channelChanged;
ChannelView &getChannelView() ChannelView &getChannelView()
@ -57,8 +53,9 @@ public:
return this->view; return this->view;
} }
const std::string &getUUID() const;
ChannelPtr getChannel() const; ChannelPtr getChannel() const;
void setChannel(ChannelPtr newChannel);
void setFlexSizeX(double x); void setFlexSizeX(double x);
double getFlexSizeX(); double getFlexSizeX();
void setFlexSizeY(double y); void setFlexSizeY(double y);
@ -98,8 +95,6 @@ private:
pajlada::Signals::Connection channelIDChangedConnection; pajlada::Signals::Connection channelIDChangedConnection;
pajlada::Signals::Connection usermodeChangedConnection; pajlada::Signals::Connection usermodeChangedConnection;
void setChannel(ChannelPtr newChannel);
void doOpenAccountPopupWidget(AccountPopupWidget *widget, QString user); void doOpenAccountPopupWidget(AccountPopupWidget *widget, QString user);
void channelNameUpdated(const QString &newChannelName); void channelNameUpdated(const QString &newChannelName);
void handleModifiers(QEvent *event, Qt::KeyboardModifiers modifiers); void handleModifiers(QEvent *event, Qt::KeyboardModifiers modifiers);

View file

@ -26,12 +26,9 @@ bool SplitContainer::isDraggingSplit = false;
Split *SplitContainer::draggingSplit = nullptr; Split *SplitContainer::draggingSplit = nullptr;
std::pair<int, int> SplitContainer::dropPosition = std::pair<int, int>(-1, -1); std::pair<int, int> SplitContainer::dropPosition = std::pair<int, int>(-1, -1);
SplitContainer::SplitContainer(Notebook *parent, NotebookTab *_tab, const std::string &_uuid) SplitContainer::SplitContainer(Notebook *parent, NotebookTab *_tab)
: BaseWidget(parent->themeManager, parent) : BaseWidget(parent->themeManager, parent)
, uuid(_uuid)
, settingRoot(fS("/containers/{}", this->uuid))
, tab(_tab) , tab(_tab)
, chats(fS("{}/chats", this->settingRoot))
, dropPreview(this) , dropPreview(this)
{ {
this->tab->page = this; this->tab->page = this;
@ -48,8 +45,6 @@ SplitContainer::SplitContainer(Notebook *parent, NotebookTab *_tab, const std::s
this->ui.hbox.setSpacing(1); this->ui.hbox.setSpacing(1);
this->ui.hbox.setMargin(0); this->ui.hbox.setMargin(0);
this->loadSplits();
this->refreshTitle(); this->refreshTitle();
} }
@ -147,6 +142,24 @@ const std::vector<Split *> &SplitContainer::getSplits() const
return this->splits; return this->splits;
} }
std::vector<std::vector<Split *>> SplitContainer::getColumns() const
{
std::vector<std::vector<Split *>> columns;
for (int i = 0; i < this->ui.hbox.count(); i++) {
std::vector<Split *> cells;
QLayout *vbox = this->ui.hbox.itemAt(i)->layout();
for (int j = 0; j < vbox->count(); j++) {
cells.push_back(dynamic_cast<Split *>(vbox->itemAt(j)->widget()));
}
columns.push_back(cells);
}
return columns;
}
NotebookTab *SplitContainer::getTab() const NotebookTab *SplitContainer::getTab() const
{ {
return this->tab; return this->tab;
@ -447,7 +460,7 @@ std::pair<int, int> SplitContainer::getChatPosition(const Split *chatWidget)
Split *SplitContainer::createChatWidget(const std::string &uuid) Split *SplitContainer::createChatWidget(const std::string &uuid)
{ {
auto split = new Split(this, uuid); auto split = new Split(this);
split->getChannelView().highlightedMessageReceived.connect([this] { split->getChannelView().highlightedMessageReceived.connect([this] {
// fourtf: error potentionally here // fourtf: error potentionally here
@ -459,7 +472,7 @@ Split *SplitContainer::createChatWidget(const std::string &uuid)
void SplitContainer::refreshTitle() void SplitContainer::refreshTitle()
{ {
if (!this->tab->useDefaultBehaviour) { if (!this->tab->useDefaultTitle) {
return; return;
} }
@ -467,7 +480,7 @@ void SplitContainer::refreshTitle()
bool first = true; bool first = true;
for (const auto &chatWidget : this->splits) { for (const auto &chatWidget : this->splits) {
auto channelName = chatWidget->channelName.getValue(); auto channelName = chatWidget->getChannel()->name;
if (channelName.isEmpty()) { if (channelName.isEmpty()) {
continue; continue;
} }
@ -487,83 +500,5 @@ void SplitContainer::refreshTitle()
this->tab->setTitle(newTitle); this->tab->setTitle(newTitle);
} }
void SplitContainer::loadSplits()
{
const auto hboxes = this->chats.getValue();
int column = 0;
for (const std::vector<std::string> &hbox : hboxes) {
int row = 0;
for (const std::string &chatUUID : hbox) {
Split *split = this->createChatWidget(chatUUID);
this->addToLayout(split, std::pair<int, int>(column, row));
++row;
}
++column;
}
}
template <typename Container>
static void saveFromLayout(QLayout *layout, Container &container)
{
for (int i = 0; i < layout->count(); ++i) {
auto item = layout->itemAt(i);
auto innerLayout = item->layout();
if (innerLayout != nullptr) {
std::vector<std::string> vbox;
for (int j = 0; j < innerLayout->count(); ++j) {
auto innerItem = innerLayout->itemAt(j);
auto innerWidget = innerItem->widget();
if (innerWidget == nullptr) {
assert(false);
continue;
}
Split *innerSplit = qobject_cast<Split *>(innerWidget);
vbox.push_back(innerSplit->getUUID());
}
container.push_back(vbox);
continue;
}
}
}
void SplitContainer::save()
{
auto layout = this->ui.hbox.layout();
std::vector<std::vector<std::string>> _chats;
for (int i = 0; i < layout->count(); ++i) {
auto item = layout->itemAt(i);
auto innerLayout = item->layout();
if (innerLayout != nullptr) {
std::vector<std::string> vbox;
for (int j = 0; j < innerLayout->count(); ++j) {
auto innerItem = innerLayout->itemAt(j);
auto innerWidget = innerItem->widget();
if (innerWidget == nullptr) {
assert(false);
continue;
}
Split *innerSplit = qobject_cast<Split *>(innerWidget);
vbox.push_back(innerSplit->getUUID());
}
_chats.push_back(vbox);
continue;
}
}
this->chats = _chats;
}
} // namespace widgets } // namespace widgets
} // namespace chatterino } // namespace chatterino

View file

@ -19,21 +19,14 @@ class SplitContainer : public BaseWidget
{ {
Q_OBJECT Q_OBJECT
const std::string uuid;
const std::string settingRoot;
public: public:
SplitContainer(Notebook *parent, NotebookTab *_tab, const std::string &_uuid); SplitContainer(Notebook *parent, NotebookTab *_tab);
const std::string &getUUID() const
{
return this->uuid;
}
std::pair<int, int> removeFromLayout(Split *widget); std::pair<int, int> removeFromLayout(Split *widget);
void addToLayout(Split *widget, std::pair<int, int> position = std::pair<int, int>(-1, -1)); void addToLayout(Split *widget, std::pair<int, int> position = std::pair<int, int>(-1, -1));
const std::vector<Split *> &getSplits() const; const std::vector<Split *> &getSplits() const;
std::vector<std::vector<Split *>> getColumns() const;
NotebookTab *getTab() const; NotebookTab *getTab() const;
void addChat(bool openChannelNameDialog = false, std::string chatUUID = std::string()); void addChat(bool openChannelNameDialog = false, std::string chatUUID = std::string());
@ -90,8 +83,6 @@ private:
std::vector<Split *> splits; std::vector<Split *> splits;
std::vector<DropRegion> dropRegions; std::vector<DropRegion> dropRegions;
pajlada::Settings::Setting<std::vector<std::vector<std::string>>> chats;
NotebookPageDropPreview dropPreview; NotebookPageDropPreview dropPreview;
void setPreviewRect(QPoint mousePos); void setPreviewRect(QPoint mousePos);

View file

@ -18,13 +18,11 @@
namespace chatterino { namespace chatterino {
namespace widgets { namespace widgets {
Window::Window(const QString &windowName, singletons::ThemeManager &_themeManager, Window::Window(singletons::ThemeManager &_themeManager, WindowType _type)
bool _isMainWindow)
: BaseWindow(_themeManager, nullptr, true) : BaseWindow(_themeManager, nullptr, true)
, settingRoot(fS("/windows/{}", windowName)) , type(_type)
, windowGeometry(this->settingRoot)
, dpi(this->getScale()) , dpi(this->getScale())
, notebook(this, _isMainWindow, this->settingRoot) , notebook(this, !this->hasCustomWindowFrame())
{ {
singletons::AccountManager::getInstance().Twitch.currentUsername.connect( singletons::AccountManager::getInstance().Twitch.currentUsername.connect(
[this](const std::string &newUsername, auto) { [this](const std::string &newUsername, auto) {
@ -35,7 +33,7 @@ Window::Window(const QString &windowName, singletons::ThemeManager &_themeManage
} }
}); });
if (this->hasCustomWindowFrame()) { if (this->hasCustomWindowFrame() && _type == Window::Main) {
this->addTitleBarButton(TitleBarButton::Settings, [] { this->addTitleBarButton(TitleBarButton::Settings, [] {
singletons::WindowManager::getInstance().showSettingsDialog(); singletons::WindowManager::getInstance().showSettingsDialog();
}); });
@ -49,6 +47,12 @@ Window::Window(const QString &windowName, singletons::ThemeManager &_themeManage
}); });
} }
if (_type == Window::Main) {
this->resize((int)(600 * this->getScale()), (int)(500 * this->getScale()));
} else {
this->resize((int)(300 * this->getScale()), (int)(500 * this->getScale()));
}
QVBoxLayout *layout = new QVBoxLayout(this); QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(&this->notebook); layout->addWidget(&this->notebook);
@ -59,8 +63,6 @@ Window::Window(const QString &windowName, singletons::ThemeManager &_themeManage
this->themeRefreshEvent(); this->themeRefreshEvent();
this->loadGeometry();
/// Initialize program-wide hotkeys /// Initialize program-wide hotkeys
// CTRL+P: Open Settings Dialog // CTRL+P: Open Settings Dialog
CreateWindowShortcut(this, "CTRL+P", [] { SettingsDialog::showDialog(); }); CreateWindowShortcut(this, "CTRL+P", [] { SettingsDialog::showDialog(); });
@ -77,7 +79,7 @@ Window::Window(const QString &windowName, singletons::ThemeManager &_themeManage
CreateWindowShortcut(this, "CTRL+9", [this] { this->notebook.selectIndex(8); }); CreateWindowShortcut(this, "CTRL+9", [this] { this->notebook.selectIndex(8); });
// CTRL+SHIFT+T: New tab // CTRL+SHIFT+T: New tab
CreateWindowShortcut(this, "CTRL+SHIFT+T", [this] { this->notebook.addNewPage(); }); CreateWindowShortcut(this, "CTRL+SHIFT+T", [this] { this->notebook.addNewPage(true); });
// CTRL+SHIFT+W: Close current tab // CTRL+SHIFT+W: Close current tab
CreateWindowShortcut(this, "CTRL+SHIFT+W", [this] { this->notebook.removeCurrentPage(); }); CreateWindowShortcut(this, "CTRL+SHIFT+W", [this] { this->notebook.removeCurrentPage(); });
@ -102,9 +104,14 @@ Window::Window(const QString &windowName, singletons::ThemeManager &_themeManage
// }); // });
} }
Window::WindowType Window::getType()
{
return this->type;
}
void Window::repaintVisibleChatWidgets(Channel *channel) void Window::repaintVisibleChatWidgets(Channel *channel)
{ {
auto *page = this->notebook.getSelectedPage(); auto *page = this->notebook.getOrAddSelectedPage();
if (page == nullptr) { if (page == nullptr) {
return; return;
@ -127,18 +134,6 @@ void Window::refreshWindowTitle(const QString &username)
this->setWindowTitle(username + " - Chatterino for Twitch"); this->setWindowTitle(username + " - Chatterino for Twitch");
} }
void Window::closeEvent(QCloseEvent *)
{
const QRect &geom = this->geometry();
this->windowGeometry.x = geom.x();
this->windowGeometry.y = geom.y();
this->windowGeometry.width = geom.width();
this->windowGeometry.height = geom.height();
this->closed.invoke();
}
bool Window::event(QEvent *e) bool Window::event(QEvent *e)
{ {
switch (e->type()) { switch (e->type()) {
@ -146,7 +141,7 @@ bool Window::event(QEvent *e)
break; break;
case QEvent::WindowDeactivate: { case QEvent::WindowDeactivate: {
auto page = this->notebook.getSelectedPage(); auto page = this->notebook.getOrAddSelectedPage();
if (page != nullptr) { if (page != nullptr) {
std::vector<Split *> splits = page->getSplits(); std::vector<Split *> splits = page->getSplits();
@ -160,35 +155,14 @@ bool Window::event(QEvent *e)
return BaseWindow::event(e); return BaseWindow::event(e);
} }
void Window::loadGeometry() void Window::closeEvent(QCloseEvent *event)
{ {
bool doSetGeometry = false; if (this->type == Window::Main) {
QRect loadedGeometry; singletons::WindowManager::getInstance().save();
if (!this->windowGeometry.x.isDefaultValue() && !this->windowGeometry.y.isDefaultValue()) { singletons::WindowManager::getInstance().closeAll();
loadedGeometry.setX(this->windowGeometry.x);
loadedGeometry.setY(this->windowGeometry.y);
doSetGeometry = true;
} }
if (!this->windowGeometry.width.isDefaultValue() && this->closed.invoke();
!this->windowGeometry.height.isDefaultValue()) {
loadedGeometry.setWidth(this->windowGeometry.width);
loadedGeometry.setHeight(this->windowGeometry.height);
} else {
loadedGeometry.setWidth(1280);
loadedGeometry.setHeight(720);
}
if (doSetGeometry) {
this->setGeometry(loadedGeometry);
} else {
this->resize(loadedGeometry.width(), loadedGeometry.height());
}
}
void Window::save()
{
this->notebook.save();
} }
} // namespace widgets } // namespace widgets

View file

@ -18,32 +18,14 @@ class ThemeManager;
namespace widgets { namespace widgets {
struct WindowGeometry {
WindowGeometry(const std::string &settingPrefix)
: x(fS("{}/geometry/x", settingPrefix))
, y(fS("{}/geometry/y", settingPrefix))
, width(fS("{}/geometry/width", settingPrefix))
, height(fS("{}/geometry/height", settingPrefix))
{
}
pajlada::Settings::Setting<int> x;
pajlada::Settings::Setting<int> y;
pajlada::Settings::Setting<int> width;
pajlada::Settings::Setting<int> height;
};
class Window : public BaseWindow class Window : public BaseWindow
{ {
Q_OBJECT Q_OBJECT
std::string settingRoot;
WindowGeometry windowGeometry;
public: public:
explicit Window(const QString &windowName, singletons::ThemeManager &_themeManager, enum WindowType { Main, Popup };
bool isMainWindow);
explicit Window(singletons::ThemeManager &_themeManager, WindowType type);
void repaintVisibleChatWidgets(Channel *channel = nullptr); void repaintVisibleChatWidgets(Channel *channel = nullptr);
@ -53,17 +35,19 @@ public:
pajlada::Signals::NoArgSignal closed; pajlada::Signals::NoArgSignal closed;
WindowType getType();
protected: protected:
void closeEvent(QCloseEvent *event) override; void closeEvent(QCloseEvent *event) override;
bool event(QEvent *event) override; bool event(QEvent *event) override;
private: private:
WindowType type;
float dpi; float dpi;
void loadGeometry(); void loadGeometry();
Notebook notebook; Notebook notebook;
// TitleBar titleBar;
friend class Notebook; friend class Notebook;