mirror of
https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System.git
synced 2024-12-27 00:11:33 +08:00
516 lines
14 KiB
C++
516 lines
14 KiB
C++
/*******************************************************************************
|
|
** 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 AutoHideDockContainer.h
|
|
/// \author Syarif Fakhri
|
|
/// \date 05.09.2022
|
|
/// \brief Implementation of CAutoHideDockContainer class
|
|
//============================================================================
|
|
|
|
//============================================================================
|
|
// INCLUDES
|
|
//============================================================================
|
|
#include <AutoHideDockContainer.h>
|
|
#include "DockManager.h"
|
|
#include "DockWidgetSideTab.h"
|
|
#include "DockWidgetTab.h"
|
|
#include "SideTabBar.h"
|
|
#include "DockAreaWidget.h"
|
|
#include "DockingStateReader.h"
|
|
#include "ResizeHandle.h"
|
|
|
|
#include <QXmlStreamWriter>
|
|
#include <QBoxLayout>
|
|
#include <QPainter>
|
|
#include <QSplitter>
|
|
#include <QPointer>
|
|
#include <QApplication>
|
|
|
|
#include <iostream>
|
|
|
|
namespace ads
|
|
{
|
|
static const int ResizeMargin = 4;
|
|
|
|
//============================================================================
|
|
bool static isHorizontalArea(SideBarLocation Area)
|
|
{
|
|
switch (Area)
|
|
{
|
|
case SideBarLocation::Top:
|
|
case SideBarLocation::Bottom: return true;
|
|
case SideBarLocation::Left:
|
|
case SideBarLocation::Right: return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//============================================================================
|
|
Qt::Edge static edgeFromSideTabBarArea(SideBarLocation Area)
|
|
{
|
|
switch (Area)
|
|
{
|
|
case SideBarLocation::Top: return Qt::BottomEdge;
|
|
case SideBarLocation::Bottom: return Qt::TopEdge;
|
|
case SideBarLocation::Left: return Qt::RightEdge;
|
|
case SideBarLocation::Right: return Qt::LeftEdge;
|
|
}
|
|
|
|
return Qt::LeftEdge;
|
|
}
|
|
|
|
|
|
//============================================================================
|
|
int resizeHandleLayoutPosition(SideBarLocation Area)
|
|
{
|
|
switch (Area)
|
|
{
|
|
case SideBarLocation::Bottom:
|
|
case SideBarLocation::Right: return 0;
|
|
|
|
case SideBarLocation::Top:
|
|
case SideBarLocation::Left: return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Private data of CAutoHideDockContainer - pimpl
|
|
*/
|
|
struct AutoHideDockContainerPrivate
|
|
{
|
|
CAutoHideDockContainer* _this;
|
|
CDockAreaWidget* DockArea{nullptr};
|
|
CDockWidget* DockWidget{nullptr};
|
|
QPointer<CDockManager> DockManager{nullptr};
|
|
SideBarLocation SideTabBarArea;
|
|
QBoxLayout* Layout;
|
|
CResizeHandle* ResizeHandle = nullptr;
|
|
QSize Size; // creates invalid size
|
|
|
|
/**
|
|
* Private data constructor
|
|
*/
|
|
AutoHideDockContainerPrivate(CAutoHideDockContainer *_public);
|
|
|
|
/**
|
|
* Convenience function to get a dock widget area
|
|
*/
|
|
DockWidgetArea getDockWidgetArea(SideBarLocation area)
|
|
{
|
|
switch (area)
|
|
{
|
|
case SideBarLocation::Left: return LeftDockWidgetArea;
|
|
case SideBarLocation::Right: return RightDockWidgetArea;
|
|
case SideBarLocation::Bottom: return BottomDockWidgetArea;
|
|
case SideBarLocation::Top: return TopDockWidgetArea;
|
|
}
|
|
|
|
return LeftDockWidgetArea;
|
|
}
|
|
|
|
/**
|
|
* Update the resize limit of the resize handle
|
|
*/
|
|
void updateResizeHandleSizeLimitMax()
|
|
{
|
|
auto Rect = _this->parentContainer()->contentRect();
|
|
ResizeHandle->setMaxResizeSize(ResizeHandle->orientation() == Qt::Horizontal
|
|
? Rect.width() : Rect.height());
|
|
}
|
|
|
|
/**
|
|
* Convenience function to check, if this is an horizontal area
|
|
*/
|
|
bool isHorizontal() const
|
|
{
|
|
return isHorizontalArea(SideTabBarArea);
|
|
}
|
|
|
|
}; // struct AutoHideDockContainerPrivate
|
|
|
|
|
|
//============================================================================
|
|
AutoHideDockContainerPrivate::AutoHideDockContainerPrivate(
|
|
CAutoHideDockContainer *_public) :
|
|
_this(_public)
|
|
{
|
|
|
|
}
|
|
|
|
|
|
//============================================================================
|
|
CDockContainerWidget* CAutoHideDockContainer::parentContainer() const
|
|
{
|
|
return internal::findParent<CDockContainerWidget*>(this);
|
|
}
|
|
|
|
|
|
//============================================================================
|
|
CAutoHideDockContainer::CAutoHideDockContainer(CDockManager* DockManager, SideBarLocation area, CDockContainerWidget* parent) :
|
|
Super(parent),
|
|
d(new AutoHideDockContainerPrivate(this))
|
|
{
|
|
d->DockManager = DockManager;
|
|
d->SideTabBarArea = area;
|
|
d->DockArea = new CDockAreaWidget(DockManager, parent);
|
|
d->DockArea->setObjectName("autoHideDockArea");
|
|
d->DockArea->setAutoHideDockContainer(this);
|
|
d->DockArea->updateAutoHideButtonCheckState();
|
|
d->DockArea->updateTitleBarButtonToolTip();
|
|
|
|
setObjectName("autoHideDockContainer");
|
|
|
|
d->Layout = new QBoxLayout(isHorizontalArea(area) ? QBoxLayout::TopToBottom : QBoxLayout::LeftToRight);
|
|
d->Layout->setContentsMargins(0, 0, 0, 0);
|
|
d->Layout->setSpacing(0);
|
|
setLayout(d->Layout);
|
|
d->Layout->addWidget(d->DockArea);
|
|
d->ResizeHandle = new CResizeHandle(edgeFromSideTabBarArea(area), this);
|
|
d->ResizeHandle->setMinResizeSize(64);
|
|
bool OpaqueResize = CDockManager::testConfigFlag(CDockManager::OpaqueSplitterResize);
|
|
d->ResizeHandle->setOpaqueResize(OpaqueResize);
|
|
d->Layout->insertWidget(resizeHandleLayoutPosition(area), d->ResizeHandle);
|
|
d->Size = d->DockArea->size();
|
|
|
|
|
|
updateSize();
|
|
parent->registerAutoHideWidget(this);
|
|
}
|
|
|
|
|
|
//============================================================================
|
|
CAutoHideDockContainer::CAutoHideDockContainer(CDockWidget* DockWidget, SideBarLocation area, CDockContainerWidget* parent) :
|
|
CAutoHideDockContainer(DockWidget->dockManager(), area, parent)
|
|
{
|
|
addDockWidget(DockWidget);
|
|
}
|
|
|
|
|
|
//============================================================================
|
|
void CAutoHideDockContainer::updateSize()
|
|
{
|
|
auto dockContainerParent = parentContainer();
|
|
auto rect = dockContainerParent->contentRect();
|
|
qDebug() << "Size " << d->Size;
|
|
|
|
switch (sideTabBarArea())
|
|
{
|
|
case SideBarLocation::Top:
|
|
resize(rect.width(), qMin(rect.height(), d->Size.height()));
|
|
move(rect.topLeft());
|
|
break;
|
|
|
|
case SideBarLocation::Left:
|
|
resize(qMin(d->Size.width(), rect.width()), rect.height());
|
|
move(rect.topLeft());
|
|
break;
|
|
|
|
case SideBarLocation::Right:
|
|
{
|
|
resize(qMin(d->Size.width(), rect.width()), rect.height());
|
|
QPoint p = rect.topRight();
|
|
p.rx() -= (width() - 1);
|
|
move(p);
|
|
}
|
|
break;
|
|
|
|
case SideBarLocation::Bottom:
|
|
{
|
|
resize(rect.width(), qMin(rect.height(), d->Size.height()));
|
|
QPoint p = rect.bottomLeft();
|
|
p.ry() -= (height() - 1);
|
|
move(p);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
CAutoHideDockContainer::~CAutoHideDockContainer()
|
|
{
|
|
ADS_PRINT("~CAutoHideDockContainer");
|
|
|
|
// Remove event filter in case there are any queued messages
|
|
qApp->removeEventFilter(this);
|
|
if (d->DockManager)
|
|
{
|
|
parentContainer()->removeAutoHideWidget(this);
|
|
}
|
|
|
|
delete d;
|
|
}
|
|
|
|
//============================================================================
|
|
CSideTabBar* CAutoHideDockContainer::sideTabBar() const
|
|
{
|
|
return parentContainer()->sideTabBar(d->SideTabBarArea);
|
|
}
|
|
|
|
//============================================================================
|
|
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->SideTabBarArea);
|
|
// The initial size should be a little bit bigger than the original dock
|
|
// area size to prevent that the resize handle of this auto hid dock area
|
|
// is near of the splitter of the old dock area.
|
|
d->Size = OldDockArea->size() + QSize(16, 16);
|
|
|
|
updateSize();
|
|
}
|
|
|
|
|
|
//============================================================================
|
|
SideBarLocation CAutoHideDockContainer::sideTabBarArea() const
|
|
{
|
|
return d->SideTabBarArea;
|
|
}
|
|
|
|
//============================================================================
|
|
CDockAreaWidget* CAutoHideDockContainer::dockAreaWidget() const
|
|
{
|
|
return d->DockArea;
|
|
}
|
|
|
|
//============================================================================
|
|
void CAutoHideDockContainer::moveContentsToParent()
|
|
{
|
|
cleanupAndDelete();
|
|
// If we unpin the auto hide tock widget, then we insert it into the same
|
|
// location like it had as a auto hide widget. This brings the least surprise
|
|
// to the user and he does not have to search where the widget was inserted.
|
|
parentContainer()->addDockWidget(d->getDockWidgetArea(d->SideTabBarArea), d->DockWidget);
|
|
parentContainer()->removeDockArea(d->DockArea);
|
|
}
|
|
|
|
|
|
//============================================================================
|
|
void CAutoHideDockContainer::cleanupAndDelete()
|
|
{
|
|
const auto dockWidget = d->DockWidget;
|
|
if (dockWidget)
|
|
{
|
|
dockWidget->sideTabWidget()->removeFromSideTabBar();
|
|
dockWidget->sideTabWidget()->setParent(dockWidget);
|
|
dockWidget->sideTabWidget()->hide();
|
|
}
|
|
|
|
hide();
|
|
deleteLater();
|
|
}
|
|
|
|
|
|
//============================================================================
|
|
void CAutoHideDockContainer::saveState(QXmlStreamWriter& s)
|
|
{
|
|
s.writeAttribute("SideTabBarArea", QString::number(sideTabBarArea()));
|
|
s.writeAttribute("Size", QString::number(d->isHorizontal() ? d->Size.height() : d->Size.width()));
|
|
|
|
qDebug() << ": saveState Size: " << d->Size;
|
|
qDebug() << ": saveState Size " << QString::number(d->isHorizontal() ? d->Size.height() : d->Size.width());
|
|
}
|
|
|
|
|
|
//============================================================================
|
|
bool CAutoHideDockContainer::restoreState(CDockingStateReader& s, bool Testing)
|
|
{
|
|
bool ok;
|
|
int Size = s.attributes().value("Size").toInt(&ok);
|
|
if (!ok)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (Testing)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (d->isHorizontal())
|
|
{
|
|
d->Size.setHeight(Size);
|
|
}
|
|
else
|
|
{
|
|
d->Size.setWidth(Size);
|
|
qDebug() << ": restoreState Width " << Size;
|
|
}
|
|
|
|
qDebug() << ": restoreState Size: " << d->Size;
|
|
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)
|
|
{
|
|
if (Enable)
|
|
{
|
|
hide();
|
|
qApp->removeEventFilter(this);
|
|
}
|
|
else
|
|
{
|
|
updateSize();
|
|
d->updateResizeHandleSizeLimitMax();
|
|
raise();
|
|
show();
|
|
d->DockManager->setDockWidgetFocused(d->DockWidget);
|
|
qApp->installEventFilter(this);
|
|
}
|
|
|
|
ADS_PRINT("CAutoHideDockContainer::collapseView " << Enable);
|
|
d->DockWidget->sideTabWidget()->updateStyle();
|
|
}
|
|
|
|
|
|
//============================================================================
|
|
void CAutoHideDockContainer::toggleCollapseState()
|
|
{
|
|
collapseView(isVisible());
|
|
}
|
|
|
|
|
|
//============================================================================
|
|
bool CAutoHideDockContainer::eventFilter(QObject* watched, QEvent* event)
|
|
{
|
|
if (event->type() == QEvent::Resize)
|
|
{
|
|
if (!d->ResizeHandle->isResizing())
|
|
{
|
|
updateSize();
|
|
}
|
|
}
|
|
else if (event->type() == QEvent::MouseButtonPress)
|
|
{
|
|
auto Container = parentContainer();
|
|
// First we check, if the mouse button press is inside the container
|
|
// 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 IsContainer = false;
|
|
while (widget)
|
|
{
|
|
if (widget == Container)
|
|
{
|
|
IsContainer = true;
|
|
}
|
|
widget = widget->parentWidget();
|
|
}
|
|
|
|
if (!IsContainer)
|
|
{
|
|
return Super::eventFilter(watched, event);
|
|
}
|
|
|
|
// Now we check, if the user clicked inside of this auto hide container.
|
|
// If the click is inside of this auto hide container, then we can also
|
|
// ignore the event, because the auto hide overlay should not get collapsed if
|
|
// user works in it
|
|
QMouseEvent* me = static_cast<QMouseEvent*>(event);
|
|
auto pos = mapFromGlobal(me->globalPos());
|
|
if (rect().contains(pos))
|
|
{
|
|
return Super::eventFilter(watched, event);
|
|
}
|
|
|
|
// Now check, if the user clicked into the side tab and ignore this event,
|
|
// because the side tab click handler will call collapseView(). If we
|
|
// do not ignore this here, then we will collapse the container and the side tab
|
|
// click handler will uncollapse it
|
|
auto SideTab = d->DockWidget->sideTabWidget();
|
|
pos = SideTab->mapFromGlobal(me->globalPos());
|
|
if (SideTab->rect().contains(pos))
|
|
{
|
|
return Super::eventFilter(watched, event);
|
|
}
|
|
|
|
// If the mouse button down event is in the dock manager but outside
|
|
// of the open auto hide container, then the auto hide dock widget
|
|
// should get collapsed
|
|
collapseView(true);
|
|
}
|
|
|
|
return Super::eventFilter(watched, event);
|
|
}
|
|
|
|
|
|
//============================================================================
|
|
void CAutoHideDockContainer::resizeEvent(QResizeEvent* event)
|
|
{
|
|
Super::resizeEvent(event);
|
|
if (d->ResizeHandle->isResizing())
|
|
{
|
|
d->Size = this->size();
|
|
d->updateResizeHandleSizeLimitMax();
|
|
}
|
|
}
|
|
|
|
}
|
|
|