Split up Window Layout loading into a loading and application stage (#1964)
* Split up Window Layout loading into a loading and application stage Previously, we were creating UI elements at while we were reading the window-layout.json file. We now read the window-layout.json file fully first, which results in a WindowLayout struct which is built up of a list of windows with a list of tabs with a root node which contains containers and splits. This WindowLayout can then be applied. This will enable PRs like #1940 to start Chatterino with Window Layouts that aren't defined in a json file. This commit has deprecated loading of v1 window layouts (we're now on v2). If a v1 window layout is there, it will just be ignored and Chatterino will boot up as if it did not have a window layout at all, and on save that old window layout will be gone. * Fix compile error for mac
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
#include "common/WindowDescriptors.hpp"
|
||||
|
||||
#include "widgets/Window.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
namespace {
|
||||
|
||||
QJsonArray loadWindowArray(const QString &settingsPath)
|
||||
{
|
||||
QFile file(settingsPath);
|
||||
file.open(QIODevice::ReadOnly);
|
||||
QByteArray data = file.readAll();
|
||||
QJsonDocument document = QJsonDocument::fromJson(data);
|
||||
QJsonArray windows_arr = document.object().value("windows").toArray();
|
||||
return windows_arr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T loadNodes(const QJsonObject &obj)
|
||||
{
|
||||
static_assert("loadNodes must be called with the SplitNodeDescriptor "
|
||||
"or ContainerNodeDescriptor type");
|
||||
}
|
||||
|
||||
template <>
|
||||
SplitNodeDescriptor loadNodes(const QJsonObject &root)
|
||||
{
|
||||
SplitNodeDescriptor descriptor;
|
||||
|
||||
descriptor.flexH_ = root.value("flexh").toDouble(1.0);
|
||||
descriptor.flexV_ = root.value("flexv").toDouble(1.0);
|
||||
|
||||
auto data = root.value("data").toObject();
|
||||
|
||||
SplitDescriptor::loadFromJSON(descriptor, root, data);
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
template <>
|
||||
ContainerNodeDescriptor loadNodes(const QJsonObject &root)
|
||||
{
|
||||
ContainerNodeDescriptor descriptor;
|
||||
|
||||
descriptor.flexH_ = root.value("flexh").toDouble(1.0);
|
||||
descriptor.flexV_ = root.value("flexv").toDouble(1.0);
|
||||
|
||||
descriptor.vertical_ = root.value("type").toString() == "vertical";
|
||||
|
||||
for (QJsonValue _val : root.value("items").toArray())
|
||||
{
|
||||
auto _obj = _val.toObject();
|
||||
|
||||
auto _type = _obj.value("type");
|
||||
if (_type == "split")
|
||||
{
|
||||
descriptor.items_.emplace_back(
|
||||
loadNodes<SplitNodeDescriptor>(_obj));
|
||||
}
|
||||
else
|
||||
{
|
||||
descriptor.items_.emplace_back(
|
||||
loadNodes<ContainerNodeDescriptor>(_obj));
|
||||
}
|
||||
}
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void SplitDescriptor::loadFromJSON(SplitDescriptor &descriptor,
|
||||
const QJsonObject &root,
|
||||
const QJsonObject &data)
|
||||
{
|
||||
descriptor.type_ = data.value("type").toString();
|
||||
descriptor.server_ = data.value("server").toInt(-1);
|
||||
if (data.contains("channel"))
|
||||
{
|
||||
descriptor.channelName_ = data.value("channel").toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
descriptor.channelName_ = data.value("name").toString();
|
||||
}
|
||||
}
|
||||
|
||||
WindowLayout WindowLayout::loadFromFile(const QString &path)
|
||||
{
|
||||
WindowLayout layout;
|
||||
|
||||
bool hasSetAMainWindow = false;
|
||||
|
||||
// "deserialize"
|
||||
for (const QJsonValue &window_val : loadWindowArray(path))
|
||||
{
|
||||
QJsonObject window_obj = window_val.toObject();
|
||||
|
||||
WindowDescriptor window;
|
||||
|
||||
// Load window type
|
||||
QString type_val = window_obj.value("type").toString();
|
||||
auto type = type_val == "main" ? WindowType::Main : WindowType::Popup;
|
||||
|
||||
if (type == WindowType::Main)
|
||||
{
|
||||
if (hasSetAMainWindow)
|
||||
{
|
||||
qDebug()
|
||||
<< "Window Layout file contains more than one Main window "
|
||||
"- demoting to Popup type";
|
||||
type = WindowType::Popup;
|
||||
}
|
||||
hasSetAMainWindow = true;
|
||||
}
|
||||
|
||||
window.type_ = type;
|
||||
|
||||
// Load window state
|
||||
if (window_obj.value("state") == "minimized")
|
||||
{
|
||||
window.state_ = WindowDescriptor::State::Minimized;
|
||||
}
|
||||
else if (window_obj.value("state") == "maximized")
|
||||
{
|
||||
window.state_ = WindowDescriptor::State::Maximized;
|
||||
}
|
||||
|
||||
// Load window 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);
|
||||
|
||||
window.geometry_ = QRect(x, y, width, height);
|
||||
}
|
||||
|
||||
bool hasSetASelectedTab = false;
|
||||
|
||||
// Load window tabs
|
||||
QJsonArray tabs = window_obj.value("tabs").toArray();
|
||||
for (QJsonValue tab_val : tabs)
|
||||
{
|
||||
TabDescriptor tab;
|
||||
|
||||
QJsonObject tab_obj = tab_val.toObject();
|
||||
|
||||
// Load tab custom title
|
||||
QJsonValue title_val = tab_obj.value("title");
|
||||
if (title_val.isString())
|
||||
{
|
||||
tab.customTitle_ = title_val.toString();
|
||||
}
|
||||
|
||||
// Load tab selected state
|
||||
tab.selected_ = tab_obj.value("selected").toBool(false);
|
||||
|
||||
if (tab.selected_)
|
||||
{
|
||||
if (hasSetASelectedTab)
|
||||
{
|
||||
qDebug() << "Window contains more than one selected tab - "
|
||||
"demoting to unselected";
|
||||
tab.selected_ = false;
|
||||
}
|
||||
hasSetASelectedTab = true;
|
||||
}
|
||||
|
||||
// Load tab "highlightsEnabled" state
|
||||
tab.highlightsEnabled_ =
|
||||
tab_obj.value("highlightsEnabled").toBool(true);
|
||||
|
||||
QJsonObject splitRoot = tab_obj.value("splits2").toObject();
|
||||
|
||||
// Load tab splits
|
||||
if (!splitRoot.isEmpty())
|
||||
{
|
||||
// root type
|
||||
auto nodeType = splitRoot.value("type").toString();
|
||||
if (nodeType == "split")
|
||||
{
|
||||
tab.rootNode_ = loadNodes<SplitNodeDescriptor>(splitRoot);
|
||||
}
|
||||
else if (nodeType == "horizontal" || nodeType == "vertical")
|
||||
{
|
||||
tab.rootNode_ =
|
||||
loadNodes<ContainerNodeDescriptor>(splitRoot);
|
||||
}
|
||||
}
|
||||
|
||||
window.tabs_.emplace_back(std::move(tab));
|
||||
}
|
||||
|
||||
// Load emote popup position
|
||||
QJsonObject emote_popup_obj = window_obj.value("emotePopup").toObject();
|
||||
layout.emotePopupPos_ = QPoint(emote_popup_obj.value("x").toInt(),
|
||||
emote_popup_obj.value("y").toInt());
|
||||
|
||||
layout.windows_.emplace_back(std::move(window));
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
Reference in New Issue
Block a user