/*******************************************************************************
** Qt Advanced Docking System
** Copyright (C) 2017 Uwe Kindler
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library 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
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; If not, see .
******************************************************************************/
//============================================================================
/// \file DockWidgetTab.h
/// \author Syarif Fakhri
/// \date 05.09.2022
/// \brief Implementation of COverlayDockContainer class
//============================================================================
//============================================================================
// INCLUDES
//============================================================================
#include "OverlayDockContainer.h"
#include "DockManager.h"
#include "DockWidgetSideTab.h"
#include "DockWidgetTab.h"
#include "SideTabBar.h"
#include "DockAreaTitleBar.h"
#include "DockAreaWidget.h"
#include "DockingStateReader.h"
#include
#include
#include
#include
#include
#include
namespace ads
{
struct OverlayDockContainerPrivate
{
COverlayDockContainer* _this;
CDockAreaWidget* DockArea{nullptr};
CDockWidget* DockWidget{nullptr};
QPointer DockManager{nullptr};
QSplitter* Splitter;
CDockWidgetSideTab::SideTabBarArea Area;
/**
* Private data constructor
*/
OverlayDockContainerPrivate(COverlayDockContainer *_public);
/**
* Convenience function to get a dock widget area
*/
DockWidgetArea getArea(CDockWidgetSideTab::SideTabBarArea area)
{
switch (area)
{
case CDockWidgetSideTab::Left:
{
return LeftDockWidgetArea;
}
case CDockWidgetSideTab::Right:
{
return RightDockWidgetArea;
}
case CDockWidgetSideTab::Bottom:
{
return BottomDockWidgetArea;
}
}
return LeftDockWidgetArea;
}
}; // struct OverlayDockContainerPrivate
//============================================================================
OverlayDockContainerPrivate::OverlayDockContainerPrivate(
COverlayDockContainer *_public) :
_this(_public)
{
}
CDockContainerWidget* COverlayDockContainer::parentContainer() const
{
return qobject_cast(parent());
}
//============================================================================
COverlayDockContainer::COverlayDockContainer(CDockManager* DockManager, CDockWidgetSideTab::SideTabBarArea area, CDockContainerWidget* parent) :
QFrame(parent),
d(new OverlayDockContainerPrivate(this))
{
d->DockManager = DockManager;
d->Area = area;
d->DockArea = new CDockAreaWidget(DockManager, parent);
d->DockArea->setObjectName("OverlayDockArea");
d->DockArea->setOverlayDockContainer(this);
d->DockArea->updateAutoHideButtonCheckState();
d->DockArea->updateTitleBarButtonToolTip();
QBoxLayout* l = new QBoxLayout(QBoxLayout::LeftToRight);
d->Splitter = new QSplitter(area == CDockWidgetSideTab::Bottom ? Qt::Orientation::Vertical : Qt::Orientation::Horizontal);
d->Splitter->setObjectName("overlaySplitter");
d->Splitter->setChildrenCollapsible(false);
const auto emptyWidget = new QWidget();
emptyWidget->setMinimumWidth(50);
emptyWidget->setMinimumHeight(50); // Prevents you from dragging the splitter too far
switch (area)
{
case CDockWidgetSideTab::Left:
{
d->Splitter->addWidget(d->DockArea);
d->Splitter->addWidget(emptyWidget);
break;
}
case CDockWidgetSideTab::Right:
{
d->Splitter->addWidget(emptyWidget);
d->Splitter->addWidget(d->DockArea);
break;
}
case CDockWidgetSideTab::Bottom:
{
d->Splitter->addWidget(emptyWidget);
d->Splitter->addWidget(d->DockArea);
break;
}
}
l->setContentsMargins(QMargins());
l->setSpacing(0);
l->addWidget(d->Splitter);
setLayout(l);
updateMask();
updateSize();
parent->registerOverlayWidget(this);
d->DockArea->installEventFilter(this);
parent->installEventFilter(this);
}
//============================================================================
void COverlayDockContainer::updateMask()
{
const auto rect = d->DockArea->frameGeometry();
const auto topLeft = rect.topLeft();
const auto handleSize = d->Splitter->handleWidth();
if (d->Area == CDockWidgetSideTab::Bottom)
{
setMask(QRect(QPoint(topLeft.x(), topLeft.y() - handleSize), QSize(rect.size().width(), rect.size().height() + handleSize)));
}
else
{
const auto offset = d->Area == CDockWidgetSideTab::SideTabBarArea::Left ? 0 : handleSize;
setMask(QRect(QPoint(topLeft.x() - offset, topLeft.y()), QSize(rect.size().width() + handleSize, rect.size().height())));
}
}
//============================================================================
void COverlayDockContainer::updateSize()
{
const auto dockContainerParent = parentContainer();
const auto rootSplitter = dockContainerParent->rootSplitter();
const auto rect = rootSplitter->frameGeometry();
move(rect.topLeft());
resize(rect.width(), rect.height());
}
//============================================================================
COverlayDockContainer::COverlayDockContainer(CDockWidget* DockWidget, CDockWidgetSideTab::SideTabBarArea area, CDockContainerWidget* parent) :
COverlayDockContainer(DockWidget->dockManager(), area, parent)
{
addDockWidget(DockWidget);
setDockSizeProportion(DockWidget->DefaultOverlayDockProportion());
}
//============================================================================
COverlayDockContainer::~COverlayDockContainer()
{
ADS_PRINT("~COverlayDockContainer");
// Remove event filter in case there are any queued messages
parent()->removeEventFilter(this);
if (d->DockManager)
{
parentContainer()->removeOverlayWidget(this);
}
delete d;
}
//============================================================================
CSideTabBar* COverlayDockContainer::sideTabBar() const
{
return parentContainer()->sideTabBar(d->Area);
}
//============================================================================
CDockWidget* COverlayDockContainer::dockWidget() const
{
return d->DockWidget;
}
//============================================================================
void COverlayDockContainer::addDockWidget(CDockWidget* DockWidget)
{
if (d->DockWidget)
{
// Remove the old dock widget at this area
d->DockArea->removeDockWidget(d->DockWidget);
}
d->DockWidget = DockWidget;
CDockAreaWidget* OldDockArea = DockWidget->dockAreaWidget();
if (OldDockArea)
{
OldDockArea->removeDockWidget(DockWidget);
}
d->DockArea->addDockWidget(DockWidget);
d->DockWidget->sideTabWidget()->updateOrientationAndSpacing(d->Area);
updateSize();
updateMask();
}
//============================================================================
void COverlayDockContainer::setDockSizeProportion(double SplitterProportion)
{
if (SplitterProportion < 0 || SplitterProportion > 1)
{
ADS_PRINT("SplitterProportion must be set between 0 and 1.");
return;
}
const auto dockSize = static_cast(static_cast(INT_MAX) * SplitterProportion);
const auto remainingSize = INT_MAX - dockSize;
switch (d->Area)
{
case CDockWidgetSideTab::Left:
{
d->Splitter->setSizes({ dockSize, remainingSize });
break;
}
case CDockWidgetSideTab::Right:
[[fallthrough]];
case CDockWidgetSideTab::Bottom:
{
d->Splitter->setSizes({ remainingSize, dockSize });
break;
}
}
}
//============================================================================
CDockWidgetSideTab::SideTabBarArea COverlayDockContainer::sideTabBarArea() const
{
return d->Area;
}
//============================================================================
CDockAreaWidget* COverlayDockContainer::dockAreaWidget() const
{
return d->DockArea;
}
//============================================================================
void COverlayDockContainer::moveContentsToParent()
{
const auto position = mapToGlobal(d->Area == CDockWidgetSideTab::Left ? QPoint(1,height() / 2) : QPoint(width() - 1, height() / 2));
const auto dockAreaWidget = parentContainer()->dockAreaAt(position);
if (dockAreaWidget != nullptr && !dockAreaWidget->containsCentralWidget())
{
parentContainer()->addDockWidget(CenterDockWidgetArea, d->DockWidget, dockAreaWidget);
}
else
{
parentContainer()->addDockWidget(d->getArea(d->Area), d->DockWidget);
}
cleanupAndDelete();
}
//============================================================================
void COverlayDockContainer::cleanupAndDelete()
{
const auto dockWidget = d->DockWidget;
if (dockWidget)
{
dockWidget->sideTabWidget()->removeFromSideTabBar();
dockWidget->sideTabWidget()->setParent(dockWidget);
dockWidget->sideTabWidget()->hide();
}
hide();
deleteLater();
}
//============================================================================
void COverlayDockContainer::saveState(QXmlStreamWriter& s)
{
s.writeAttribute("SideTabBarArea", QString::number(sideTabBarArea()));
QStringList Sizes;
for (auto Size : d->Splitter->sizes())
{
Sizes << QString::number(Size);
}
s.writeAttribute("Sizes", Sizes.join(" "));
}
//============================================================================
bool COverlayDockContainer::restoreState(CDockingStateReader& s, bool Testing)
{
auto sSizes = s.attributes().value("Sizes").trimmed().toString();
ADS_PRINT("Sizes: " << sSizes);
QTextStream TextStream(&sSizes);
QList Sizes;
while (!TextStream.atEnd())
{
int value;
TextStream >> value;
Sizes.append(value);
}
if (Sizes.count() != d->Splitter->count())
{
return false;
}
if (!Testing)
{
d->Splitter->setSizes(Sizes);
}
return true;
}
void COverlayDockContainer::toggleView(bool Enable)
{
if (Enable)
{
const auto dockWidget = d->DockWidget;
if (dockWidget)
{
dockWidget->sideTabWidget()->show();
}
}
else
{
const auto dockWidget = d->DockWidget;
if (dockWidget)
{
dockWidget->sideTabWidget()->hide();
}
hide();
}
}
void COverlayDockContainer::collapseView(bool Enable)
{
if (Enable)
{
hide();
d->DockArea->hide();
d->DockWidget->hide();
}
else
{
show();
d->DockArea->show();
d->DockWidget->show();
}
}
//============================================================================
bool COverlayDockContainer::areaExistsInConfig(CDockWidgetSideTab::SideTabBarArea area)
{
switch (area)
{
case CDockWidgetSideTab::Left:
{
return CDockManager::testConfigFlag(CDockManager::DockContainerHasLeftSideBar);
}
case CDockWidgetSideTab::Right:
{
return CDockManager::testConfigFlag(CDockManager::DockContainerHasRightSideBar);
}
case CDockWidgetSideTab::Bottom:
{
return CDockManager::testConfigFlag(CDockManager::DockContainerHasBottomSideBar);
}
}
return true;
}
//============================================================================
bool COverlayDockContainer::eventFilter(QObject* watched, QEvent* event)
{
if (event->type() == QEvent::Resize)
{
updateSize();
updateMask();
}
return QWidget::eventFilter(watched, event);
}
//============================================================================
void COverlayDockContainer::mousePressEvent(QMouseEvent* event)
{
QWidget::mousePressEvent(event);
}
//============================================================================
void COverlayDockContainer::resizeEvent(QResizeEvent* event)
{
updateMask();
QWidget::resizeEvent(event);
}
}