1
0
mirror of https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System.git synced 2025-03-14 18:19:52 +08:00
Qt-Advanced-Docking-System/src/AutoHideDockContainer.cpp

546 lines
15 KiB
C++
Raw Normal View History

/*******************************************************************************
** 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 <http://www.gnu.org/licenses/>.
******************************************************************************/
//============================================================================
/// \file DockWidgetTab.h
/// \author Syarif Fakhri
/// \date 05.09.2022
/// \brief Implementation of COverlayDockContainer class
//============================================================================
//============================================================================
// INCLUDES
//============================================================================
#include <AutoHideDockContainer.h>
#include "DockManager.h"
#include "DockWidgetSideTab.h"
#include "DockWidgetTab.h"
#include "SideTabBar.h"
#include "DockAreaWidget.h"
#include "DockingStateReader.h"
#include <QXmlStreamWriter>
#include <QBoxLayout>
#include <QPainter>
#include <QSplitter>
#include <QPointer>
#include <QApplication>
#include <iostream>
namespace ads
{
struct AutoHideDockContainerPrivate
{
CAutoHideDockContainer* _this;
CDockAreaWidget* DockArea{nullptr};
CDockWidget* DockWidget{nullptr};
QPointer<CDockManager> DockManager{nullptr};
CDockWidgetSideTab::SideTabBarArea Area;
bool Collapsed = true;
bool Ignore = false;
/**
* Private data constructor
*/
AutoHideDockContainerPrivate(CAutoHideDockContainer *_public);
2022-09-13 10:42:58 +08:00
/**
* Convenience function to get a dock widget area
*/
DockWidgetArea getArea(CDockWidgetSideTab::SideTabBarArea area)
{
switch (area)
{
2022-09-15 12:51:59 +08:00
case CDockWidgetSideTab::LeftBottom:
case CDockWidgetSideTab::LeftTop:
2022-09-13 10:42:58 +08:00
{
return LeftDockWidgetArea;
}
2022-09-15 12:51:59 +08:00
case CDockWidgetSideTab::RightBottom:
case CDockWidgetSideTab::RightTop:
2022-09-13 10:42:58 +08:00
{
return RightDockWidgetArea;
}
case CDockWidgetSideTab::Bottom:
{
return BottomDockWidgetArea;
}
}
return LeftDockWidgetArea;
}
2022-09-15 12:51:59 +08:00
/*
* Convenience function to get dock position
*/
QPoint getSimplifiedDockAreaPosition() const
{
switch (Area)
{
case CDockWidgetSideTab::LeftTop:
case CDockWidgetSideTab::LeftBottom:
{
return QPoint(1, _this->height() / 2);
}
case CDockWidgetSideTab::RightTop:
case CDockWidgetSideTab::RightBottom:
{
return QPoint(_this->width() - 1, _this->height() / 2);
}
case CDockWidgetSideTab::Bottom:
{
return QPoint(_this->width() / 2, _this->height() - 1);
}
}
return QPoint();
}
}; // struct OverlayDockContainerPrivate
//============================================================================
AutoHideDockContainerPrivate::AutoHideDockContainerPrivate(
CAutoHideDockContainer *_public) :
_this(_public)
{
}
CDockContainerWidget* CAutoHideDockContainer::parentContainer() const
{
return internal::findParent<CDockContainerWidget*>(this);
}
//============================================================================
CAutoHideDockContainer::CAutoHideDockContainer(CDockManager* DockManager, CDockWidgetSideTab::SideTabBarArea area, CDockContainerWidget* parent) :
QSplitter(area == CDockWidgetSideTab::Bottom ? Qt::Orientation::Vertical : Qt::Orientation::Horizontal, parent),
d(new AutoHideDockContainerPrivate(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();
setObjectName("overlaySplitter");
setChildrenCollapsible(false);
const auto emptyWidget = new QWidget();
2022-09-13 10:42:58 +08:00
emptyWidget->setMinimumWidth(50);
emptyWidget->setMinimumHeight(50); // Prevents you from dragging the splitter too far
2022-09-13 10:42:58 +08:00
switch (area)
{
2022-09-15 12:51:59 +08:00
case CDockWidgetSideTab::LeftBottom:
case CDockWidgetSideTab::LeftTop:
2022-09-13 10:42:58 +08:00
{
addWidget(d->DockArea);
addWidget(emptyWidget);
2022-09-13 10:42:58 +08:00
break;
}
2022-09-15 12:51:59 +08:00
case CDockWidgetSideTab::RightBottom:
case CDockWidgetSideTab::RightTop:
2022-09-13 10:42:58 +08:00
{
addWidget(emptyWidget);
addWidget(d->DockArea);
2022-09-13 10:42:58 +08:00
break;
}
case CDockWidgetSideTab::Bottom:
{
addWidget(emptyWidget);
addWidget(d->DockArea);
2022-09-13 10:42:58 +08:00
break;
}
}
updateMask();
updateSize();
parent->registerOverlayWidget(this);
d->DockArea->installEventFilter(this);
parent->installEventFilter(this);
}
//============================================================================
void CAutoHideDockContainer::updateMask()
{
2022-09-13 10:42:58 +08:00
const auto rect = d->DockArea->frameGeometry();
const auto topLeft = rect.topLeft();
const auto handleSize = handleWidth();
2022-09-13 10:42:58 +08:00
if (d->Area == CDockWidgetSideTab::Bottom)
{
setMask(QRect(QPoint(topLeft.x(), topLeft.y() - handleSize), QSize(rect.size().width(), rect.size().height() + handleSize)));
2022-09-15 12:51:59 +08:00
return;
2022-09-13 10:42:58 +08:00
}
2022-09-15 12:51:59 +08:00
auto offset = 0;
if (d->Area == CDockWidgetSideTab::SideTabBarArea::RightTop || d->Area == CDockWidgetSideTab::SideTabBarArea::RightBottom)
{
offset = handleSize;
}
setMask(QRect(QPoint(topLeft.x() - offset, topLeft.y()), QSize(rect.size().width() + handleSize, rect.size().height())));
}
//============================================================================
void CAutoHideDockContainer::updateSize()
{
const auto dockContainerParent = parentContainer();
const auto rootSplitter = dockContainerParent->rootSplitter();
const auto rect = rootSplitter->frameGeometry();
move(rect.topLeft());
resize(rect.width(), rect.height());
}
//============================================================================
CAutoHideDockContainer::CAutoHideDockContainer(CDockWidget* DockWidget, CDockWidgetSideTab::SideTabBarArea area, CDockContainerWidget* parent) :
CAutoHideDockContainer(DockWidget->dockManager(), area, parent)
{
addDockWidget(DockWidget);
setDockSizeProportion(DockWidget->DefaultOverlayDockProportion());
}
//============================================================================
CAutoHideDockContainer::~CAutoHideDockContainer()
{
ADS_PRINT("~COverlayDockContainer");
2022-09-14 13:10:06 +08:00
// Remove event filter in case there are any queued messages
d->DockArea->removeEventFilter(this);
parent()->removeEventFilter(this);
if (d->DockManager)
{
parentContainer()->removeOverlayWidget(this);
}
delete d;
}
//============================================================================
CSideTabBar* CAutoHideDockContainer::sideTabBar() const
{
return parentContainer()->sideTabBar(d->Area);
}
//============================================================================
CDockWidget* CAutoHideDockContainer::dockWidget() const
{
return d->DockWidget;
}
//============================================================================
void CAutoHideDockContainer::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 CAutoHideDockContainer::setDockSizeProportion(float SplitterProportion)
{
if (SplitterProportion < 0 || SplitterProportion > 1)
{
ADS_PRINT("SplitterProportion must be set between 0 and 1.");
return;
}
2022-09-15 09:43:15 +08:00
const auto dockSize = static_cast<int>(static_cast<float>(INT_MAX) * SplitterProportion);
const auto remainingSize = INT_MAX - dockSize;
2022-09-13 10:42:58 +08:00
switch (d->Area)
{
2022-09-15 12:51:59 +08:00
case CDockWidgetSideTab::LeftBottom:
case CDockWidgetSideTab::LeftTop:
2022-09-13 10:42:58 +08:00
{
setSizes({ dockSize, remainingSize });
2022-09-13 10:42:58 +08:00
break;
}
2022-09-15 12:51:59 +08:00
case CDockWidgetSideTab::RightBottom:
case CDockWidgetSideTab::RightTop:
2022-09-13 10:42:58 +08:00
case CDockWidgetSideTab::Bottom:
{
setSizes({ remainingSize, dockSize });
2022-09-13 10:42:58 +08:00
break;
}
}
}
//============================================================================
CDockWidgetSideTab::SideTabBarArea CAutoHideDockContainer::sideTabBarArea() const
{
return d->Area;
}
//============================================================================
CDockAreaWidget* CAutoHideDockContainer::dockAreaWidget() const
{
return d->DockArea;
}
//============================================================================
void CAutoHideDockContainer::moveContentsToParent()
{
cleanupAndDelete();
2022-09-15 12:51:59 +08:00
const auto position = mapToGlobal(d->getSimplifiedDockAreaPosition());
const auto dockAreaWidget = parentContainer()->dockAreaAt(position);
if (dockAreaWidget != nullptr && !dockAreaWidget->containsCentralWidget())
{
parentContainer()->addDockWidget(CenterDockWidgetArea, d->DockWidget, dockAreaWidget);
}
else
{
2022-09-13 10:42:58 +08:00
parentContainer()->addDockWidget(d->getArea(d->Area), d->DockWidget);
}
parentContainer()->removeDockArea(d->DockArea);
}
2022-09-08 16:30:07 +08:00
//============================================================================
void CAutoHideDockContainer::cleanupAndDelete()
{
const auto dockWidget = d->DockWidget;
if (dockWidget)
{
dockWidget->sideTabWidget()->removeFromSideTabBar();
dockWidget->sideTabWidget()->setParent(dockWidget);
dockWidget->sideTabWidget()->hide();
}
hide();
deleteLater();
}
2022-09-08 16:30:07 +08:00
//============================================================================
void CAutoHideDockContainer::saveState(QXmlStreamWriter& s)
{
s.writeAttribute("SideTabBarArea", QString::number(sideTabBarArea()));
QStringList Sizes;
for (auto Size : sizes())
{
Sizes << QString::number(Size);
}
s.writeAttribute("Sizes", Sizes.join(" "));
}
2022-09-08 16:30:07 +08:00
//============================================================================
bool CAutoHideDockContainer::restoreState(CDockingStateReader& s, bool Testing)
{
auto sSizes = s.attributes().value("Sizes").trimmed().toString();
ADS_PRINT("Sizes: " << sSizes);
QTextStream TextStream(&sSizes);
QList<int> Sizes;
while (!TextStream.atEnd())
{
int value;
TextStream >> value;
Sizes.append(value);
}
if (Sizes.count() != count())
{
return false;
}
if (!Testing)
{
setSizes(Sizes);
}
return true;
}
void CAutoHideDockContainer::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();
qApp->removeEventFilter(this);
}
}
void CAutoHideDockContainer::collapseView(bool Enable)
{
std::cout << "COverlayDockContainer::collapseView " << Enable << " "
<< d->DockWidget->objectName().toStdString() << std::endl;
if (d->Ignore)
{
return;
}
if (Enable)
{
hide();
d->DockArea->hide();
d->DockWidget->hide();
}
else
{
2022-09-15 11:51:56 +08:00
raise();
show();
d->DockArea->show();
d->DockWidget->show();
qApp->installEventFilter(this);
}
d->Collapsed = Enable;
}
//============================================================================
void CAutoHideDockContainer::toggleCollapseState()
{
collapseView(isVisible());
}
2022-09-08 16:30:07 +08:00
//============================================================================
bool CAutoHideDockContainer::areaExistsInConfig(CDockWidgetSideTab::SideTabBarArea area)
{
2022-09-13 10:42:58 +08:00
switch (area)
{
2022-09-15 12:51:59 +08:00
case CDockWidgetSideTab::LeftBottom:
case CDockWidgetSideTab::LeftTop:
2022-09-13 10:42:58 +08:00
{
return CDockManager::testConfigFlag(CDockManager::DockContainerHasLeftSideBar);
}
2022-09-15 12:51:59 +08:00
case CDockWidgetSideTab::RightBottom:
case CDockWidgetSideTab::RightTop:
2022-09-13 10:42:58 +08:00
{
return CDockManager::testConfigFlag(CDockManager::DockContainerHasRightSideBar);
}
case CDockWidgetSideTab::Bottom:
{
return CDockManager::testConfigFlag(CDockManager::DockContainerHasBottomSideBar);
}
}
return true;
}
2022-09-08 16:30:07 +08:00
//============================================================================
bool CAutoHideDockContainer::eventFilter(QObject* watched, QEvent* event)
{
if (event->type() == QEvent::Resize)
{
updateSize();
updateMask();
}
else if (event->type() == QEvent::MouseButtonPress)
{
// First we check, if the mouse button press is inside the dock manager
// widget. If it is not, i.e. if someone resizes the main window or
// clicks into the application menu or toolbar, then we ignore the
// event
auto widget = qobject_cast<QWidget*>(watched);
bool IsDockManager = false;
while (widget)
{
if (widget == d->DockManager)
{
IsDockManager = true;
}
widget = widget->parentWidget();
}
if (!IsDockManager)
{
return QSplitter::eventFilter(watched, event);
}
// Now we check, if the user clicked inside of this overlay. If the click
// is inside of the overlay, the we can also ignore the event, because
// the overlay should not get collapsed if user works in it
QMouseEvent* me = static_cast<QMouseEvent*>(event);
auto pos = d->DockArea->mapFromGlobal(me->globalPos());
auto rect = d->DockArea->rect().adjusted(-handleWidth(), 0, 0, 0);
if (rect.contains(pos))
{
return QSplitter::eventFilter(watched, event);
}
// If the mouse button down event is in the dock manager but outside
// of the open overlay container, then the overlay dock widget
// should get collapsed
collapseView(true);
d->Ignore = true;
}
else if (event->type() == QEvent::MouseButtonRelease)
{
std::cout << "Mouse release: " << watched->metaObject()->className() << std::endl;
d->Ignore = false;
if (!isVisible())
{
qApp->removeEventFilter(this);
}
}
return QSplitter::eventFilter(watched, event);
}
2022-09-08 16:30:07 +08:00
//============================================================================
void CAutoHideDockContainer::resizeEvent(QResizeEvent* event)
{
updateMask();
QSplitter::resizeEvent(event);
}
}