2018-08-24 19:41:58 +08:00
|
|
|
//============================================================================
|
|
|
|
/// \file DockAreaTabBar.cpp
|
|
|
|
/// \author Uwe Kindler
|
|
|
|
/// \date 24.08.2018
|
|
|
|
/// \brief Implementation of CDockAreaTabBar class
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
// INCLUDES
|
|
|
|
//============================================================================
|
|
|
|
#include "DockAreaTabBar.h"
|
|
|
|
|
|
|
|
#include <QMouseEvent>
|
|
|
|
#include <QScrollBar>
|
|
|
|
#include <QDebug>
|
2018-10-10 21:15:59 +08:00
|
|
|
#include <QBoxLayout>
|
2018-10-11 14:54:32 +08:00
|
|
|
#include <QMenu>
|
2018-08-24 19:41:58 +08:00
|
|
|
|
|
|
|
#include "FloatingDockContainer.h"
|
|
|
|
#include "DockAreaWidget.h"
|
|
|
|
#include "DockOverlay.h"
|
|
|
|
#include "DockManager.h"
|
2018-09-07 17:10:14 +08:00
|
|
|
#include "DockWidget.h"
|
2018-10-10 21:15:59 +08:00
|
|
|
#include "DockWidgetTab.h"
|
|
|
|
|
|
|
|
#include <iostream>
|
2018-08-24 19:41:58 +08:00
|
|
|
|
|
|
|
namespace ads
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Private data class of CDockAreaTabBar class (pimpl)
|
|
|
|
*/
|
|
|
|
struct DockAreaTabBarPrivate
|
|
|
|
{
|
|
|
|
CDockAreaTabBar* _this;
|
|
|
|
QPoint DragStartMousePos;
|
|
|
|
CDockAreaWidget* DockArea;
|
|
|
|
CFloatingDockContainer* FloatingWidget = nullptr;
|
2018-10-10 21:15:59 +08:00
|
|
|
QWidget* TabsContainerWidget;
|
|
|
|
QBoxLayout* TabsLayout;
|
|
|
|
int CurrentIndex = -1;
|
2018-10-11 14:54:32 +08:00
|
|
|
bool MenuOutdated = true;
|
|
|
|
QMenu* TabsMenu;
|
2018-08-24 19:41:58 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Private data constructor
|
|
|
|
*/
|
|
|
|
DockAreaTabBarPrivate(CDockAreaTabBar* _public);
|
2018-10-12 16:41:19 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Update tabs after current index changed or when tabs are removed.
|
|
|
|
* The function reassigns the stylesheet to update the tabs
|
|
|
|
*/
|
|
|
|
void updateTabs();
|
2018-08-24 19:41:58 +08:00
|
|
|
};
|
|
|
|
// struct DockAreaTabBarPrivate
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
DockAreaTabBarPrivate::DockAreaTabBarPrivate(CDockAreaTabBar* _public) :
|
|
|
|
_this(_public)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-10-12 16:41:19 +08:00
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void DockAreaTabBarPrivate::updateTabs()
|
|
|
|
{
|
|
|
|
// Set active TAB and update all other tabs to be inactive
|
|
|
|
for (int i = 0; i < _this->count(); ++i)
|
|
|
|
{
|
|
|
|
auto TabWidget = _this->tab(i);
|
|
|
|
if (!TabWidget)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == CurrentIndex)
|
|
|
|
{
|
|
|
|
TabWidget->show();
|
|
|
|
TabWidget->setActiveTab(true);
|
|
|
|
_this->ensureWidgetVisible(TabWidget);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TabWidget->setActiveTab(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-24 19:41:58 +08:00
|
|
|
//============================================================================
|
|
|
|
CDockAreaTabBar::CDockAreaTabBar(CDockAreaWidget* parent) :
|
|
|
|
QScrollArea(parent),
|
|
|
|
d(new DockAreaTabBarPrivate(this))
|
|
|
|
{
|
|
|
|
d->DockArea = parent;
|
|
|
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Ignored);
|
|
|
|
setFrameStyle(QFrame::NoFrame);
|
|
|
|
setWidgetResizable(true);
|
|
|
|
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
|
|
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
2018-10-10 21:15:59 +08:00
|
|
|
|
|
|
|
d->TabsContainerWidget = new QWidget();
|
|
|
|
d->TabsContainerWidget->setObjectName("tabsContainerWidget");
|
|
|
|
setWidget(d->TabsContainerWidget);
|
|
|
|
|
|
|
|
d->TabsLayout = new QBoxLayout(QBoxLayout::LeftToRight);
|
|
|
|
d->TabsLayout->setContentsMargins(0, 0, 0, 0);
|
|
|
|
d->TabsLayout->setSpacing(0);
|
|
|
|
d->TabsLayout->addStretch(1);
|
|
|
|
d->TabsContainerWidget->setLayout(d->TabsLayout);
|
2018-08-24 19:41:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
CDockAreaTabBar::~CDockAreaTabBar()
|
|
|
|
{
|
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void CDockAreaTabBar::wheelEvent(QWheelEvent* Event)
|
|
|
|
{
|
|
|
|
Event->accept();
|
|
|
|
const int direction = Event->angleDelta().y();
|
|
|
|
if (direction < 0)
|
|
|
|
{
|
|
|
|
horizontalScrollBar()->setValue(horizontalScrollBar()->value() + 20);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
horizontalScrollBar()->setValue(horizontalScrollBar()->value() - 20);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void CDockAreaTabBar::mousePressEvent(QMouseEvent* ev)
|
|
|
|
{
|
2018-10-11 14:54:32 +08:00
|
|
|
std::cout << "CDockAreaTabBar::mousePressEvent" << std::endl;
|
2018-08-24 19:41:58 +08:00
|
|
|
if (ev->button() == Qt::LeftButton)
|
|
|
|
{
|
|
|
|
ev->accept();
|
|
|
|
d->DragStartMousePos = ev->pos();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
QScrollArea::mousePressEvent(ev);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void CDockAreaTabBar::mouseReleaseEvent(QMouseEvent* ev)
|
|
|
|
{
|
|
|
|
if (ev->button() == Qt::LeftButton)
|
|
|
|
{
|
|
|
|
qDebug() << "CTabsScrollArea::mouseReleaseEvent";
|
|
|
|
ev->accept();
|
|
|
|
d->FloatingWidget = nullptr;
|
|
|
|
d->DragStartMousePos = QPoint();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
QScrollArea::mouseReleaseEvent(ev);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void CDockAreaTabBar::mouseMoveEvent(QMouseEvent* ev)
|
|
|
|
{
|
|
|
|
QScrollArea::mouseMoveEvent(ev);
|
|
|
|
if (ev->buttons() != Qt::LeftButton)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (d->FloatingWidget)
|
|
|
|
{
|
|
|
|
d->FloatingWidget->moveFloating();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If this is the last dock area in a dock container it does not make
|
|
|
|
// sense to move it to a new floating widget and leave this one
|
|
|
|
// empty
|
|
|
|
if (d->DockArea->dockContainer()->isFloating()
|
|
|
|
&& d->DockArea->dockContainer()->visibleDockAreaCount() == 1)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this->geometry().contains(ev->pos()))
|
|
|
|
{
|
|
|
|
qDebug() << "CTabsScrollArea::startFloating";
|
|
|
|
startFloating(d->DragStartMousePos);
|
|
|
|
auto Overlay = d->DockArea->dockManager()->containerOverlay();
|
|
|
|
Overlay->setAllowedAreas(OuterDockAreas);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void CDockAreaTabBar::mouseDoubleClickEvent(QMouseEvent *event)
|
|
|
|
{
|
|
|
|
// If this is the last dock area in a dock container it does not make
|
|
|
|
// sense to move it to a new floating widget and leave this one
|
|
|
|
// empty
|
|
|
|
if (d->DockArea->dockContainer()->isFloating() && d->DockArea->dockContainer()->dockAreaCount() == 1)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
startFloating(event->pos());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void CDockAreaTabBar::startFloating(const QPoint& Pos)
|
|
|
|
{
|
|
|
|
QSize Size = d->DockArea->size();
|
|
|
|
CFloatingDockContainer* FloatingWidget = new CFloatingDockContainer(d->DockArea);
|
|
|
|
FloatingWidget->startFloating(Pos, Size);
|
|
|
|
d->FloatingWidget = FloatingWidget;
|
2018-09-26 15:57:36 +08:00
|
|
|
auto TopLevelDockWidget = d->FloatingWidget->topLevelDockWidget();
|
|
|
|
if (TopLevelDockWidget)
|
2018-09-07 17:10:14 +08:00
|
|
|
{
|
2018-09-26 15:57:36 +08:00
|
|
|
TopLevelDockWidget->emitTopLevelChanged(true);
|
2018-09-07 17:10:14 +08:00
|
|
|
}
|
2018-08-24 19:41:58 +08:00
|
|
|
}
|
2018-10-10 21:15:59 +08:00
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void CDockAreaTabBar::setCurrentIndex(int index)
|
|
|
|
{
|
2018-10-11 16:55:36 +08:00
|
|
|
std::cout << "CDockAreaTabBar::setCurrentIndex " << index << std::endl;
|
2018-10-10 21:15:59 +08:00
|
|
|
if (index == d->CurrentIndex)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-10-11 21:23:19 +08:00
|
|
|
if (index < 0 || index > (count() - 1))
|
2018-10-10 21:15:59 +08:00
|
|
|
{
|
|
|
|
qWarning() << Q_FUNC_INFO << "Invalid index" << index;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
emit currentChanging(index);
|
|
|
|
d->CurrentIndex = index;
|
2018-10-12 16:41:19 +08:00
|
|
|
d->updateTabs();
|
2018-10-10 21:15:59 +08:00
|
|
|
emit currentChanged(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
int CDockAreaTabBar::count() const
|
|
|
|
{
|
2018-10-11 21:23:19 +08:00
|
|
|
// The tab bar contains a stretch item as last item
|
|
|
|
return d->TabsLayout->count() - 1;
|
2018-10-10 21:15:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void CDockAreaTabBar::insertTab(int Index, CDockWidgetTab* Tab)
|
|
|
|
{
|
|
|
|
d->TabsLayout->insertWidget(Index, Tab);
|
|
|
|
connect(Tab, SIGNAL(clicked()), this, SLOT(onTabClicked()));
|
2018-10-12 15:17:14 +08:00
|
|
|
connect(Tab, SIGNAL(moved(const QPoint&)), this, SLOT(onTabWidgetMoved(const QPoint&)));
|
2018-10-11 14:54:32 +08:00
|
|
|
d->MenuOutdated = true;
|
2018-10-11 15:21:01 +08:00
|
|
|
if (Index <= d->CurrentIndex)
|
|
|
|
{
|
2018-10-12 16:41:19 +08:00
|
|
|
setCurrentIndex(d->CurrentIndex++);
|
2018-10-11 15:21:01 +08:00
|
|
|
}
|
2018-10-10 21:15:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void CDockAreaTabBar::removeTab(CDockWidgetTab* Tab)
|
|
|
|
{
|
2018-10-12 16:41:19 +08:00
|
|
|
if (!count())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
qDebug() << "CDockAreaTabBar::removeTab ";
|
|
|
|
int NewCurrentIndex = currentIndex();
|
|
|
|
int RemoveIndex = d->TabsLayout->indexOf(Tab);
|
|
|
|
if (count() == 1)
|
|
|
|
{
|
|
|
|
NewCurrentIndex = -1;
|
|
|
|
}
|
|
|
|
if (NewCurrentIndex > RemoveIndex)
|
|
|
|
{
|
|
|
|
NewCurrentIndex--;
|
|
|
|
}
|
|
|
|
else if (NewCurrentIndex == RemoveIndex)
|
|
|
|
{
|
|
|
|
NewCurrentIndex = -1;
|
|
|
|
// First we walk to the right to search for the next visible tab
|
|
|
|
for (int i = (RemoveIndex + 1); i < count(); ++i)
|
|
|
|
{
|
|
|
|
if (tab(i)->isVisibleTo(this))
|
|
|
|
{
|
|
|
|
NewCurrentIndex = i - 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there is no visible tab right to this tab then we walk to
|
|
|
|
// the left to find a visible tab
|
|
|
|
if (NewCurrentIndex < 0)
|
|
|
|
{
|
|
|
|
for (int i = (RemoveIndex - 1); i >= 0; --i)
|
|
|
|
{
|
|
|
|
if (tab(i)->isVisibleTo(this))
|
|
|
|
{
|
|
|
|
NewCurrentIndex = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-10 21:15:59 +08:00
|
|
|
d->TabsLayout->removeWidget(Tab);
|
2018-10-12 15:17:14 +08:00
|
|
|
Tab->disconnect(this);
|
2018-10-11 14:54:32 +08:00
|
|
|
d->MenuOutdated = true;
|
2018-10-12 16:41:19 +08:00
|
|
|
qDebug() << "NewCurrentIndex " << NewCurrentIndex;
|
|
|
|
if (NewCurrentIndex != d->CurrentIndex)
|
|
|
|
{
|
|
|
|
setCurrentIndex(NewCurrentIndex);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
d->updateTabs();
|
|
|
|
}
|
2018-10-10 21:15:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
int CDockAreaTabBar::currentIndex() const
|
|
|
|
{
|
|
|
|
return d->CurrentIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
CDockWidgetTab* CDockAreaTabBar::currentTab() const
|
|
|
|
{
|
|
|
|
return qobject_cast<CDockWidgetTab*>(d->TabsLayout->itemAt(d->CurrentIndex)->widget());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void CDockAreaTabBar::onTabClicked()
|
|
|
|
{
|
|
|
|
CDockWidgetTab* Tab = qobject_cast<CDockWidgetTab*>(sender());
|
|
|
|
if (!Tab)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int index = d->TabsLayout->indexOf(Tab);
|
2018-10-11 16:55:36 +08:00
|
|
|
if (index < 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2018-10-10 21:15:59 +08:00
|
|
|
setCurrentIndex(index);
|
|
|
|
emit tabBarClicked(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-10-11 21:23:19 +08:00
|
|
|
//===========================================================================
|
2018-10-12 15:17:14 +08:00
|
|
|
CDockWidgetTab* CDockAreaTabBar::tab(int Index) const
|
2018-10-11 21:23:19 +08:00
|
|
|
{
|
2018-10-12 15:17:14 +08:00
|
|
|
if (Index >= count())
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return qobject_cast<CDockWidgetTab*>(d->TabsLayout->itemAt(Index)->widget());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void CDockAreaTabBar::onTabWidgetMoved(const QPoint& GlobalPos)
|
|
|
|
{
|
|
|
|
CDockWidgetTab* MovingTab = qobject_cast<CDockWidgetTab*>(sender());
|
|
|
|
if (!MovingTab)
|
2018-10-11 21:23:19 +08:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-10-12 15:17:14 +08:00
|
|
|
int fromIndex = d->TabsLayout->indexOf(MovingTab);
|
|
|
|
auto MousePos = mapFromGlobal(GlobalPos);
|
|
|
|
int toIndex = -1;
|
2018-10-11 21:23:19 +08:00
|
|
|
// Find tab under mouse
|
|
|
|
for (int i = 0; i < count(); ++i)
|
|
|
|
{
|
2018-10-12 15:17:14 +08:00
|
|
|
CDockWidgetTab* DropTab = tab(i);
|
|
|
|
if (DropTab == MovingTab || !DropTab->isVisibleTo(this)
|
|
|
|
|| !DropTab->geometry().contains(MousePos))
|
2018-10-11 21:23:19 +08:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-10-12 15:17:14 +08:00
|
|
|
toIndex = d->TabsLayout->indexOf(DropTab);
|
|
|
|
if (toIndex == fromIndex)
|
|
|
|
{
|
|
|
|
toIndex = -1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (toIndex < 0)
|
|
|
|
{
|
|
|
|
toIndex = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2018-10-11 21:23:19 +08:00
|
|
|
|
2018-10-12 15:17:14 +08:00
|
|
|
// Now check if the mouse is behind the last tab
|
|
|
|
if (toIndex < 0)
|
2018-10-11 21:23:19 +08:00
|
|
|
{
|
2018-10-12 15:17:14 +08:00
|
|
|
if (MousePos.x() > tab(count() - 1)->geometry().right())
|
2018-10-11 21:23:19 +08:00
|
|
|
{
|
2018-10-12 15:17:14 +08:00
|
|
|
qDebug() << "after all tabs";
|
|
|
|
toIndex = count() - 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
toIndex = fromIndex;
|
2018-10-11 21:23:19 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-12 15:17:14 +08:00
|
|
|
d->TabsLayout->removeWidget(MovingTab);
|
|
|
|
d->TabsLayout->insertWidget(toIndex, MovingTab);
|
|
|
|
if (toIndex >= 0)
|
2018-10-11 21:23:19 +08:00
|
|
|
{
|
2018-10-12 15:17:14 +08:00
|
|
|
qDebug() << "tabMoved from " << fromIndex << " to " << toIndex;
|
|
|
|
emit tabMoved(fromIndex, toIndex);
|
2018-10-12 16:41:19 +08:00
|
|
|
setCurrentIndex(toIndex);
|
2018-10-11 21:23:19 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-10-11 14:54:32 +08:00
|
|
|
//===========================================================================
|
|
|
|
void CDockAreaTabBar::closeTab(int Index)
|
2018-10-10 21:15:59 +08:00
|
|
|
{
|
2018-10-11 21:23:19 +08:00
|
|
|
if (Index < 0 || Index >= count())
|
2018-10-11 14:54:32 +08:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
emit tabCloseRequested(Index);
|
2018-10-10 21:15:59 +08:00
|
|
|
}
|
2018-08-24 19:41:58 +08:00
|
|
|
} // namespace ads
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// EOF DockAreaTabBar.cpp
|