Files
KDDockWidgets/src/LayoutSaver_p.h
Sergio Martins 4734f06046 builds
2020-04-02 19:43:36 +01:00

554 lines
14 KiB
C++

/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KD_LAYOUTSAVER_P_H
#define KD_LAYOUTSAVER_P_H
#include "LayoutSaver.h"
#include "KDDockWidgets.h"
#include <QRect>
#include <QDataStream>
#include <QDebug>
#include <QScreen>
#include <QApplication>
#include <QJsonDocument>
#include <memory>
#define MULTISPLITTER_LAYOUT_MAGIC_MARKER "bac9948e-5f1b-4271-acc5-07f1708e2611"
/**
* Bump whenever the format changes, so we can still load old layouts.
* version 1: Initial version
* version 2: Introduced MainWindow::screenSize and FloatingWindow::screenSize
*/
#define KDDOCKWIDGETS_SERIALIZATION_VERSION 2
namespace KDDockWidgets {
template <typename T>
typename T::List fromVariantList(const QVariantList &listV)
{
typename T::List result;
result.reserve(listV.size());
for (const QVariant &v : listV) {
T t;
t.fromVariantMap(v.toMap());
result.push_back(t);
}
return result;
}
template <typename T>
QVariantList toVariantList(const typename T::List &list)
{
QVariantList result;
result.reserve(list.size());
for (const T &v : list)
result.push_back(v.toVariantMap());
return result;
}
struct LayoutSaver::Placeholder
{
typedef QVector<LayoutSaver::Placeholder> List;
QVariantMap toVariantMap() const;
void fromVariantMap(const QVariantMap &map);
bool isFloatingWindow;
int indexOfFloatingWindow;
int itemIndex;
QString mainWindowUniqueName;
};
///@brief contains info about how a main window is scaled.
///Used for RestoreOption_RelativeToMainWindow
struct LayoutSaver::ScalingInfo
{
ScalingInfo() = default;
explicit ScalingInfo(const QString &mainWindowId, QRect savedMainWindowGeo);
bool isValid() const {
return heightFactor > 0 && widthFactor > 0 && !((qFuzzyCompare(widthFactor, 1) && qFuzzyCompare(heightFactor, 1)));
}
void translatePos(QPoint &) const;
void applyFactorsTo(QPoint &) const;
void applyFactorsTo(QSize &) const;
void applyFactorsTo(QRect &) const;
QString mainWindowName;
QRect savedMainWindowGeometry;
QRect realMainWindowGeometry;
double heightFactor = -1;
double widthFactor = -1;
};
struct LayoutSaver::LastPosition
{
QRect lastFloatingGeometry;
int tabIndex;
bool wasFloating;
LayoutSaver::Placeholder::List placeholders;
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
void scaleSizes(const ScalingInfo &scalingInfo);
QVariantMap toVariantMap() const;
void fromVariantMap(const QVariantMap &map);
};
struct DOCKS_EXPORT LayoutSaver::DockWidget
{
// Using shared ptr, as we need to modify shared instances
typedef std::shared_ptr<LayoutSaver::DockWidget> Ptr;
typedef QVector<Ptr> List;
static QHash<QString, Ptr> s_dockWidgets;
bool isValid() const;
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
void scaleSizes(const ScalingInfo &scalingInfo);
static Ptr dockWidgetForName(const QString &name)
{
auto dw = s_dockWidgets.value(name);
if (dw)
return dw;
dw = Ptr(new LayoutSaver::DockWidget);
s_dockWidgets.insert(name, dw);
dw->uniqueName = name;
return dw;
}
QVariantMap toVariantMap() const;
void fromVariantMap(const QVariantMap &map);
QString uniqueName;
QString affinityName;
LayoutSaver::LastPosition lastPosition;
private:
DockWidget() {}
};
inline QVariantList toVariantList(const LayoutSaver::DockWidget::List &list)
{
QVariantList result;
result.reserve(list.size());
for (const auto &dw : list)
result.push_back(dw->toVariantMap());
return result;
}
inline QVariantList dockWidgetNames(const LayoutSaver::DockWidget::List &list)
{
QVariantList result;
result.reserve(list.size());
for (auto &dw : list)
result.push_back(dw->uniqueName);
return result;
}
struct LayoutSaver::Frame
{
bool isValid() const;
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
void scaleSizes(const ScalingInfo &scalingInfo);
QVariantMap toVariantMap() const;
void fromVariantMap(const QVariantMap &map);
bool isNull = true;
QString objectName;
QRect geometry;
unsigned int options;
int currentTabIndex;
LayoutSaver::DockWidget::List dockWidgets;
};
struct LayoutSaver::Item
{
typedef QVector<LayoutSaver::Item> List;
bool isValid(const MultiSplitterLayout &) const;
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
void scaleSizes(const ScalingInfo &scalingInfo);
QVariantMap toVariantMap() const;
void fromVariantMap(const QVariantMap &map);
QString objectName;
bool isPlaceholder;
QRect geometry;
QSize minSize;
int indexOfLeftAnchor; // TODO: Search and replace. Remove anchors
int indexOfTopAnchor;
int indexOfRightAnchor;
int indexOfBottomAnchor;
LayoutSaver::Frame frame;
};
struct LayoutSaver::MultiSplitterLayout
{
bool isValid() const;
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
void scaleSizes(const ScalingInfo &scalingInfo);
QVariantMap toVariantMap() const;
void fromVariantMap(const QVariantMap &map);
LayoutSaver::Item::List items;
QSize minSize;
QSize size;
};
struct LayoutSaver::FloatingWindow
{
typedef QVector<LayoutSaver::FloatingWindow> List;
bool isValid() const;
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
void scaleSizes(const ScalingInfo &);
QVariantMap toVariantMap() const;
void fromVariantMap(const QVariantMap &map);
LayoutSaver::MultiSplitterLayout multiSplitterLayout;
QString affinityName;
int parentIndex = -1;
QRect geometry;
int screenIndex;
QSize screenSize; // for relative-size restoring
bool isVisible = true;
};
struct LayoutSaver::MainWindow
{
public:
typedef QVector<LayoutSaver::MainWindow> List;
bool isValid() const;
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
void scaleSizes();
QVariantMap toVariantMap() const;
void fromVariantMap(const QVariantMap &map);
KDDockWidgets::MainWindowOptions options;
LayoutSaver::MultiSplitterLayout multiSplitterLayout;
QString uniqueName;
QString affinityName;
QRect geometry;
int screenIndex;
QSize screenSize; // for relative-size restoring
bool isVisible;
ScalingInfo scalingInfo;
};
///@brief we serialize some info about screens, so eventually we can make restore smarter when switching screens
///Not used currently, but nice to have in the json already
struct LayoutSaver::ScreenInfo
{
typedef QVector<LayoutSaver::ScreenInfo> List;
QVariantMap toVariantMap() const;
void fromVariantMap(const QVariantMap &map);
int index;
QRect geometry;
QString name;
double devicePixelRatio;
};
struct LayoutSaver::Layout
{
public:
Layout() {
s_currentLayoutBeingRestored = this;
const QList<QScreen*> screens = qApp->screens();
for (int i = 0; i < screens.size(); ++i) {
ScreenInfo info;
info.index = i;
info.geometry = screens[i]->geometry();
info.name = screens[i]->name();
info.devicePixelRatio = screens[i]->devicePixelRatio();
screenInfo.push_back(info);
}
}
~Layout() {
s_currentLayoutBeingRestored = nullptr;
}
bool isValid() const;
bool fillFrom(const QByteArray &serialized);
QByteArray toJson() const;
bool fromJson(const QByteArray &jsonData);
QVariantMap toVariantMap() const;
void fromVariantMap(const QVariantMap &map);
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
void scaleSizes();
friend QDataStream &operator>>(QDataStream &ds, LayoutSaver::Frame *frame);
static LayoutSaver::Layout* s_currentLayoutBeingRestored;
LayoutSaver::MainWindow mainWindowForIndex(int index) const;
int serializationVersion = KDDOCKWIDGETS_SERIALIZATION_VERSION;
LayoutSaver::MainWindow::List mainWindows;
LayoutSaver::FloatingWindow::List floatingWindows;
LayoutSaver::DockWidget::List closedDockWidgets;
LayoutSaver::DockWidget::List allDockWidgets;
ScreenInfo::List screenInfo;
};
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::ScreenInfo *info)
{
ds >> info->index;
ds >> info->geometry;
ds >> info->name;
ds >> info->devicePixelRatio;
return ds;
}
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Placeholder *p)
{
ds >> p->isFloatingWindow;
if (p->isFloatingWindow)
ds >> p->indexOfFloatingWindow;
else
ds >> p->mainWindowUniqueName;
ds >> p->itemIndex;
return ds;
}
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Frame *frame)
{
int numDockWidgets;
frame->dockWidgets.clear();
frame->isNull = false;
ds >> frame->objectName;
ds >> frame->geometry;
if (LayoutSaver::Layout::s_currentLayoutBeingRestored->serializationVersion >= 2) {
QSize sz;
ds >> sz; // deprecated field, just discard
}
ds >> frame->options;
ds >> frame->currentTabIndex;
ds >> numDockWidgets;
for (int i = 0; i < numDockWidgets; ++i) {
QString name;
ds >> name;
auto dw = LayoutSaver::DockWidget::dockWidgetForName(name);
frame->dockWidgets.push_back(dw);
}
return ds;
}
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Item *item)
{
ds >> item->objectName;
ds >> item->isPlaceholder;
ds >> item->geometry;
ds >> item->minSize;
ds >> item->indexOfLeftAnchor;
ds >> item->indexOfTopAnchor;
ds >> item->indexOfRightAnchor;
ds >> item->indexOfBottomAnchor;
bool hasFrame;
ds >> hasFrame;
if (hasFrame) {
ds >> &item->frame;
item->frame.isNull = false;
} else {
item->frame.isNull = true;
}
return ds;
}
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::MultiSplitterLayout *l)
{
int numItems;
QString marker;
ds >> marker;
if (marker != QLatin1String(MULTISPLITTER_LAYOUT_MAGIC_MARKER))
qWarning() << Q_FUNC_INFO << "Corrupt stream, invalid magic";
ds >> l->size;
ds >> l->minSize;
ds >> numItems;
l->items.clear();
for (int i = 0 ; i < numItems; ++i) {
LayoutSaver::Item item;
ds >> &item;
l->items.push_back(item);
}
return ds;
}
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::LastPosition *lp)
{
int numPlaceholders;
ds >> numPlaceholders;
lp->placeholders.clear();
for (int i = 0 ; i < numPlaceholders; ++i) {
LayoutSaver::Placeholder p;
ds >> &p;
lp->placeholders.push_back(p);
}
ds >> lp->lastFloatingGeometry;
ds >> lp->tabIndex;
ds >> lp->wasFloating;
return ds;
}
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::FloatingWindow *fw)
{
ds >> fw->parentIndex;
ds >> fw->geometry;
if (LayoutSaver::Layout::s_currentLayoutBeingRestored->serializationVersion >= 2) {
ds >> fw->screenIndex;
ds >> fw->screenSize;
}
ds >> fw->isVisible;
ds >> &fw->multiSplitterLayout;
return ds;
}
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::MainWindow *m)
{
ds >> m->uniqueName;
ds >> m->geometry;
if (LayoutSaver::Layout::s_currentLayoutBeingRestored->serializationVersion >= 2) {
ds >> m->screenIndex;
ds >> m->screenSize;
}
ds >> m->isVisible;
ds >> m->options;
ds >> &m->multiSplitterLayout;
return ds;
}
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Layout *l)
{
LayoutSaver::DockWidget::s_dockWidgets.clear();
int numMainWindows;
int numFloatingWindows;
int numClosedDockWidgets;
int numAllDockWidgets;
int numScreenInfo;
ds >> l->serializationVersion;
ds >> numMainWindows;
l->mainWindows.clear();
for (int i = 0; i < numMainWindows; ++i) {
LayoutSaver::MainWindow m;
ds >> &m;
l->mainWindows.push_back(m);
}
ds >> numFloatingWindows;
l->floatingWindows.clear();
for (int i = 0; i < numFloatingWindows; ++i) {
LayoutSaver::FloatingWindow m;
ds >> &m;
l->floatingWindows.push_back(m);
}
ds >> numClosedDockWidgets;
l->closedDockWidgets.clear();
for (int i = 0; i < numClosedDockWidgets; ++i) {
QString name;
ds >> name;
auto dw = LayoutSaver::DockWidget::dockWidgetForName(name);
l->closedDockWidgets.push_back(dw);
}
ds >> numAllDockWidgets;
l->allDockWidgets.clear();
for (int i = 0; i < numAllDockWidgets; ++i) {
QString name;
ds >> name;
auto dw = LayoutSaver::DockWidget::dockWidgetForName(name);
ds >> &dw->lastPosition;
l->allDockWidgets.push_back(dw);
}
if (LayoutSaver::Layout::s_currentLayoutBeingRestored->serializationVersion >= 2) {
ds >> numScreenInfo;
l->screenInfo.clear();
l->screenInfo.reserve(numScreenInfo);
for (int i = 0; i < numScreenInfo; ++i) {
LayoutSaver::ScreenInfo info;
ds >> &info;
l->screenInfo.push_back(info);
}
}
return ds;
}
}
#endif