317 lines
7.9 KiB
C++
317 lines
7.9 KiB
C++
/*
|
|
This file is part of KDDockWidgets.
|
|
|
|
SPDX-FileCopyrightText: 2020-2022 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
|
|
Author: Sérgio Martins <sergio.martins@kdab.com>
|
|
|
|
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
|
|
|
|
Contact KDAB at <info@kdab.com> for commercial licensing options.
|
|
*/
|
|
|
|
#include "LayoutSaver_p.h"
|
|
#include "Config.h"
|
|
#include "DockWidgetBase_p.h"
|
|
#include "FloatingWindow_p.h"
|
|
#include "Frame_p.h"
|
|
#include "FrameworkWidgetFactory.h"
|
|
#include "MainWindowBase.h"
|
|
#include "Position_p.h"
|
|
#include "Utils_p.h"
|
|
|
|
#include "multisplitter/Item_p.h"
|
|
|
|
using namespace KDDockWidgets;
|
|
|
|
|
|
LayoutWidget::LayoutWidget(QWidgetOrQuick *parent)
|
|
: LayoutGuestWidget(parent)
|
|
{
|
|
}
|
|
|
|
LayoutWidget::~LayoutWidget()
|
|
{
|
|
if (m_rootItem->hostWidget()->asQObject() == this)
|
|
delete m_rootItem;
|
|
DockRegistry::self()->unregisterLayout(this);
|
|
}
|
|
|
|
bool LayoutWidget::isInMainWindow(bool honourNesting) const
|
|
{
|
|
return mainWindow(honourNesting) != nullptr;
|
|
}
|
|
|
|
MainWindowBase *LayoutWidget::mainWindow(bool honourNesting) const
|
|
{
|
|
// QtQuick doesn't support nesting yet
|
|
honourNesting = honourNesting && kddwUsesQtWidgets();
|
|
|
|
if (honourNesting) {
|
|
// This layout might be a MDIArea, nested in DropArea, which is main window.
|
|
return firstParentOfType<MainWindowBase>(this);
|
|
} else {
|
|
|
|
if (auto pw = QWidgetAdapter::parentWidget()) {
|
|
// Note that if pw is a FloatingWindow then pw->parentWidget() can be a MainWindow too, as
|
|
// it's parented
|
|
if (pw->objectName() == QLatin1String("MyCentralWidget"))
|
|
return qobject_cast<MainWindowBase *>(pw->parentWidget());
|
|
|
|
if (auto mw = qobject_cast<MainWindowBase *>(pw))
|
|
return mw;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
FloatingWindow *LayoutWidget::floatingWindow() const
|
|
{
|
|
return qobject_cast<FloatingWindow *>(QWidgetAdapter::parentWidget());
|
|
}
|
|
|
|
void LayoutWidget::setRootItem(Layouting::ItemContainer *root)
|
|
{
|
|
delete m_rootItem;
|
|
m_rootItem = root;
|
|
connect(m_rootItem, &Layouting::ItemContainer::numVisibleItemsChanged, this,
|
|
&MultiSplitter::visibleWidgetCountChanged);
|
|
connect(m_rootItem, &Layouting::ItemContainer::minSizeChanged, this,
|
|
[this] { setMinimumSize(layoutMinimumSize()); });
|
|
}
|
|
|
|
QSize LayoutWidget::layoutMinimumSize() const
|
|
{
|
|
return m_rootItem->minSize();
|
|
}
|
|
|
|
QSize LayoutWidget::layoutMaximumSizeHint() const
|
|
{
|
|
return m_rootItem->maxSizeHint();
|
|
}
|
|
|
|
void LayoutWidget::setLayoutMinimumSize(QSize sz)
|
|
{
|
|
if (sz != m_rootItem->minSize()) {
|
|
setLayoutSize(size().expandedTo(m_rootItem->minSize())); // Increase size in case we need to
|
|
m_rootItem->setMinSize(sz);
|
|
}
|
|
}
|
|
|
|
QSize LayoutWidget::size() const
|
|
{
|
|
return m_rootItem->size();
|
|
}
|
|
|
|
void LayoutWidget::clearLayout()
|
|
{
|
|
m_rootItem->clear();
|
|
}
|
|
|
|
bool LayoutWidget::checkSanity() const
|
|
{
|
|
return m_rootItem->checkSanity();
|
|
}
|
|
|
|
void LayoutWidget::dumpLayout() const
|
|
{
|
|
m_rootItem->dumpLayout();
|
|
}
|
|
|
|
void LayoutWidget::restorePlaceholder(DockWidgetBase *dw, Layouting::Item *item, int tabIndex)
|
|
{
|
|
if (item->isPlaceholder()) {
|
|
Frame *newFrame = Config::self().frameworkWidgetFactory()->createFrame(this);
|
|
item->restore(newFrame);
|
|
}
|
|
|
|
auto frame = qobject_cast<Frame *>(item->guestAsQObject());
|
|
Q_ASSERT(frame);
|
|
|
|
if (tabIndex != -1 && frame->dockWidgetCount() >= tabIndex) {
|
|
frame->insertWidget(dw, tabIndex);
|
|
} else {
|
|
frame->addWidget(dw);
|
|
}
|
|
|
|
frame->QWidgetAdapter::setVisible(true);
|
|
}
|
|
|
|
void LayoutWidget::unrefOldPlaceholders(const Frame::List &framesBeingAdded) const
|
|
{
|
|
for (Frame *frame : framesBeingAdded) {
|
|
for (DockWidgetBase *dw : frame->dockWidgets()) {
|
|
dw->d->lastPosition()->removePlaceholders(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LayoutWidget::setLayoutSize(QSize size)
|
|
{
|
|
if (size != this->size()) {
|
|
m_rootItem->setSize_recursive(size);
|
|
if (!m_inResizeEvent && !LayoutSaver::restoreInProgress())
|
|
resize(size);
|
|
}
|
|
}
|
|
|
|
const Layouting::Item::List LayoutWidget::items() const
|
|
{
|
|
return m_rootItem->items_recursive();
|
|
}
|
|
|
|
bool LayoutWidget::containsItem(const Layouting::Item *item) const
|
|
{
|
|
return m_rootItem->contains_recursive(item);
|
|
}
|
|
|
|
bool LayoutWidget::containsFrame(const Frame *frame) const
|
|
{
|
|
return itemForFrame(frame) != nullptr;
|
|
}
|
|
|
|
int LayoutWidget::count() const
|
|
{
|
|
return m_rootItem->count_recursive();
|
|
}
|
|
|
|
int LayoutWidget::visibleCount() const
|
|
{
|
|
return m_rootItem->visibleCount_recursive();
|
|
}
|
|
|
|
int LayoutWidget::placeholderCount() const
|
|
{
|
|
return count() - visibleCount();
|
|
}
|
|
|
|
Layouting::Item *LayoutWidget::itemForFrame(const Frame *frame) const
|
|
{
|
|
if (!frame)
|
|
return nullptr;
|
|
|
|
return m_rootItem->itemForWidget(frame);
|
|
}
|
|
|
|
DockWidgetBase::List LayoutWidget::dockWidgets() const
|
|
{
|
|
DockWidgetBase::List dockWidgets;
|
|
const Frame::List frames = this->frames();
|
|
for (Frame *frame : frames)
|
|
dockWidgets << frame->dockWidgets();
|
|
|
|
return dockWidgets;
|
|
}
|
|
|
|
Frame::List LayoutWidget::framesFrom(QWidgetOrQuick *frameOrMultiSplitter) const
|
|
{
|
|
if (auto frame = qobject_cast<Frame *>(frameOrMultiSplitter))
|
|
return { frame };
|
|
|
|
if (auto msw = qobject_cast<MultiSplitter *>(frameOrMultiSplitter))
|
|
return msw->frames();
|
|
|
|
return {};
|
|
}
|
|
|
|
Frame::List LayoutWidget::frames() const
|
|
{
|
|
const Layouting::Item::List items = m_rootItem->items_recursive();
|
|
|
|
Frame::List result;
|
|
result.reserve(items.size());
|
|
|
|
for (Layouting::Item *item : items) {
|
|
if (auto f = static_cast<Frame *>(item->guestAsQObject()))
|
|
result.push_back(f);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void LayoutWidget::removeItem(Layouting::Item *item)
|
|
{
|
|
if (!item) {
|
|
qWarning() << Q_FUNC_INFO << "nullptr item";
|
|
return;
|
|
}
|
|
|
|
item->parentContainer()->removeItem(item);
|
|
}
|
|
|
|
void LayoutWidget::updateSizeConstraints()
|
|
{
|
|
const QSize newMinSize = m_rootItem->minSize();
|
|
qCDebug(sizing) << Q_FUNC_INFO << "Updating size constraints from" << minimumSize() << "to"
|
|
<< newMinSize;
|
|
|
|
setLayoutMinimumSize(newMinSize);
|
|
}
|
|
|
|
bool LayoutWidget::deserialize(const LayoutSaver::MultiSplitter &l)
|
|
{
|
|
QHash<QString, Layouting::Widget *> frames;
|
|
for (const LayoutSaver::Frame &frame : qAsConst(l.frames)) {
|
|
Frame *f = Frame::deserialize(frame);
|
|
Q_ASSERT(!frame.id.isEmpty());
|
|
frames.insert(frame.id, f);
|
|
}
|
|
|
|
m_rootItem->fillFromVariantMap(l.layout, frames);
|
|
|
|
updateSizeConstraints();
|
|
|
|
// This qMin() isn't needed for QtWidgets (but harmless), but it's required for QtQuick
|
|
// as some sizing is async
|
|
const QSize newLayoutSize = QWidgetAdapter::size().expandedTo(m_rootItem->minSize());
|
|
|
|
m_rootItem->setSize_recursive(newLayoutSize);
|
|
|
|
return true;
|
|
}
|
|
|
|
void LayoutWidget::onLayoutRequest()
|
|
{
|
|
updateSizeConstraints();
|
|
}
|
|
|
|
bool LayoutWidget::onResize(QSize newSize)
|
|
{
|
|
QScopedValueRollback<bool> resizeGuard(m_inResizeEvent, true); // to avoid re-entrancy
|
|
|
|
if (!LayoutSaver::restoreInProgress()) {
|
|
// don't resize anything while we're restoring the layout
|
|
setLayoutSize(newSize);
|
|
}
|
|
|
|
return false; // So QWidget::resizeEvent is called
|
|
}
|
|
|
|
LayoutSaver::MultiSplitter LayoutWidget::serialize() const
|
|
{
|
|
LayoutSaver::MultiSplitter l;
|
|
l.layout = m_rootItem->toVariantMap();
|
|
const Layouting::Item::List items = m_rootItem->items_recursive();
|
|
l.frames.reserve(items.size());
|
|
for (Layouting::Item *item : items) {
|
|
if (!item->isContainer()) {
|
|
if (auto frame = qobject_cast<Frame *>(item->guestAsQObject()))
|
|
l.frames.insert(frame->id(), frame->serialize());
|
|
}
|
|
}
|
|
|
|
return l;
|
|
}
|
|
|
|
void LayoutWidget::onCloseEvent(QCloseEvent *e)
|
|
{
|
|
e->accept(); // Accepted by default (will close unless ignored)
|
|
|
|
const Frame::List frames = this->frames();
|
|
for (Frame *frame : frames) {
|
|
qApp->sendEvent(frame, e);
|
|
if (!e->isAccepted())
|
|
break; // Stop when the first frame prevents closing
|
|
}
|
|
}
|