Implemented proper tab handling

This commit is contained in:
Uwe Kindler 2017-02-28 15:23:02 +01:00
parent f0584ff0c5
commit c14352e7c1
7 changed files with 289 additions and 3 deletions

View File

@ -37,6 +37,7 @@
#include <QStyle>
#include <QPushButton>
#include <QDebug>
#include <QMenu>
#include "DockContainerWidget.h"
#include "DockWidget.h"
@ -46,6 +47,9 @@
namespace ads
{
static const char* const INDEX_PROPERTY = "index";
static const char* const ACTION_PROPERTY = "action";
/**
* Custom scroll bar implementation for dock area tab bar
*/
@ -104,6 +108,43 @@ struct DockAreaWidgetPrivate
* Creates the layout for top area with tabs and close button
*/
void createTabBar();
/**
* Returns the dock widget with the given index
*/
CDockWidget* dockWidgetAt(int index)
{
return dynamic_cast<CDockWidget*>(ContentsLayout->widget(index));
}
/**
* Convenience function to ease title widget access by index
*/
CDockWidgetTitleBar* titleWidgetAt(int index)
{
return dockWidgetAt(index)->titleBar();
}
/**
* Adds a tabs menu entry for the given dock widget
*/
void addTabsMenuEntry(CDockWidget* DockWidget);
/**
* Returns the tab action of the given dock widget
*/
QAction* dockWidgetTabAction(CDockWidget* DockWidget) const
{
return qvariant_cast<QAction*>(DockWidget->property(ACTION_PROPERTY));
}
/**
* Returns the index of the given dock widget
*/
int dockWidgetIndex(CDockWidget* DockWidget) const
{
return DockWidget->property(INDEX_PROPERTY).toInt();
}
};
// struct DockAreaWidgetPrivate
@ -142,7 +183,10 @@ void DockAreaWidgetPrivate::createTabBar()
TabsMenuButton->setFlat(true);
TabsMenuButton->setIcon(_this->style()->standardIcon(QStyle::SP_TitleBarUnshadeButton));
TabsMenuButton->setMaximumWidth(TabsMenuButton->iconSize().width());
TabsMenuButton->setMenu(new QMenu(TabsMenuButton));
TopLayout->addWidget(TabsMenuButton, 0);
_this->connect(TabsMenuButton->menu(), SIGNAL(triggered(QAction*)),
SLOT(onTabsMenuActionTriggered(QAction*)));
CloseButton = new QPushButton();
CloseButton->setObjectName("closeButton");
@ -156,6 +200,16 @@ void DockAreaWidgetPrivate::createTabBar()
TabsLayoutInitCount = TabsLayout->count();
}
//============================================================================
void DockAreaWidgetPrivate::addTabsMenuEntry(CDockWidget* DockWidget)
{
auto Action = TabsMenuButton->menu()->addAction(DockWidget->windowTitle());
QVariant vAction = QVariant::fromValue(Action);
DockWidget->setProperty(ACTION_PROPERTY, vAction);
}
//============================================================================
CDockAreaWidget::CDockAreaWidget(CDockManager* DockManager, CDockContainerWidget* parent) :
QFrame(parent),
@ -204,6 +258,7 @@ CDockContainerWidget* CDockAreaWidget::dockContainerWidget() const
void CDockAreaWidget::addDockWidget(CDockWidget* DockWidget)
{
d->ContentsLayout->addWidget(DockWidget);
DockWidget->titleBar()->setDockAreaWidget(this);
auto TitleBar = DockWidget->titleBar();
d->TabsLayout->insertWidget(d->TabsLayout->count() - d->TabsLayoutInitCount,
TitleBar);
@ -213,6 +268,9 @@ void CDockAreaWidget::addDockWidget(CDockWidget* DockWidget)
{
setCurrentIndex(0);
}
DockWidget->setProperty(INDEX_PROPERTY, d->ContentsLayout->count() - 1);
d->addTabsMenuEntry(DockWidget);
}
@ -259,7 +317,7 @@ void CDockAreaWidget::setCurrentIndex(int index)
TitleWidget->setActiveTab(true);
d->TabsScrollArea->ensureWidgetVisible(TitleWidget);
auto Features = TitleWidget->dockWidget()->features();
d->CloseButton->setEnabled(Features.testFlag(CDockWidget::DockWidgetClosable));
d->CloseButton->setVisible(Features.testFlag(CDockWidget::DockWidgetClosable));
}
else
{
@ -269,6 +327,114 @@ void CDockAreaWidget::setCurrentIndex(int index)
d->ContentsLayout->setCurrentIndex(index);
}
//============================================================================
QRect CDockAreaWidget::titleAreaGeometry() const
{
return d->TopLayout->geometry();
}
//============================================================================
QRect CDockAreaWidget::contentAreaGeometry() const
{
return d->ContentsLayout->geometry();
}
//============================================================================
int CDockAreaWidget::tabIndex(CDockWidget* DockWidget)
{
return d->ContentsLayout->indexOf(DockWidget);
}
//============================================================================
QList<CDockWidget*> CDockAreaWidget::dockWidgets() const
{
QList<CDockWidget*> DockWidgetList;
for (int i = 0; i < d->ContentsLayout->count(); ++i)
{
DockWidgetList.append(dockWidget(i));
}
return DockWidgetList;
}
//============================================================================
int CDockAreaWidget::indexOfContentByTitlePos(const QPoint& p, QWidget* exclude) const
{
for (int i = 0; i < d->ContentsLayout->count(); ++i)
{
auto TitleWidget = d->titleWidgetAt(i);
if (TitleWidget->geometry().contains(p) && (!exclude || TitleWidget != exclude))
{
return i;
}
}
return -1;
}
//============================================================================
int CDockAreaWidget::count() const
{
return d->ContentsLayout->count();
}
//============================================================================
CDockWidget* CDockAreaWidget::dockWidget(int Index) const
{
return dynamic_cast<CDockWidget*>(d->ContentsLayout->widget(Index));
}
//============================================================================
void CDockAreaWidget::reorderDockWidget(int fromIndex, int toIndex)
{
if (fromIndex >= d->ContentsLayout->count() || fromIndex < 0
|| toIndex >= d->ContentsLayout->count() || toIndex < 0 || fromIndex == toIndex)
{
qDebug() << "Invalid index for tab movement" << fromIndex << toIndex;
d->TabsLayout->update();
return;
}
CDockWidget* DockWidget = dockWidget(fromIndex);
// reorder tabs menu action to match new order of contents
auto Menu = d->TabsMenuButton->menu();
auto TabsAction = d->dockWidgetTabAction(DockWidget);
Menu->removeAction(TabsAction);
if (toIndex >= Menu->actions().count())
{
Menu->addAction(TabsAction);
}
else
{
Menu->insertAction(Menu->actions().at(toIndex), TabsAction);
}
// now reorder contents and title bars
QLayoutItem* liFrom = nullptr;
liFrom = d->TabsLayout->takeAt(fromIndex);
d->TabsLayout->insertItem(toIndex, liFrom);
liFrom = d->ContentsLayout->takeAt(fromIndex);
d->ContentsLayout->insertWidget(toIndex, liFrom->widget());
delete liFrom;
//Menu->removeAction()
}
//============================================================================
void CDockAreaWidget::onTabsMenuActionTriggered(QAction* Action)
{
int Index = d->TabsMenuButton->menu()->actions().indexOf(Action);
setCurrentIndex(Index);
}
} // namespace ads
//---------------------------------------------------------------------------

View File

@ -53,6 +53,7 @@ private:
private slots:
void onDockWidgetTitleClicked();
void onTabsMenuActionTriggered(QAction* Action);
public:
/**
@ -77,6 +78,47 @@ public:
*/
void addDockWidget(CDockWidget* DockWidget);
/**
* Returns the rectangle of the title area
*/
QRect titleAreaGeometry() const;
/**
* Returns the rectangle of the content
*/
QRect contentAreaGeometry() const;
/**
* Returns the tab index of the given DockWidget
*/
int tabIndex(CDockWidget* DockWidget);
/**
* Returns the index of contents of the title widget that is located at
* mouse position pos
*/
int indexOfContentByTitlePos(const QPoint& pos, QWidget* exclude = nullptr) const;
/**
* Returns a list of all dock widgets in this dock area
*/
QList<CDockWidget*> dockWidgets() const;
/**
* Returns the number of dock widgets in this area
*/
int count() const;
/**
* Returns a dock widget by its index
*/
CDockWidget* dockWidget(int Index) const;
/**
* Reorder the index position of DockWidget at fromIndx to toIndex.
*/
void reorderDockWidget(int fromIndex, int toIndex);
public slots:
/**
* This sets the index position of the current tab page.

View File

@ -43,7 +43,7 @@ class CDockManager;
/**
* Container that manages a number of dock areas with single dock widgets
* or tabyfied dock widtes in each area
* or tabyfied dock widgets in each area
*/
class CDockContainerWidget : public QFrame
{

View File

@ -124,6 +124,7 @@ CDockWidget::DockWidgetFeatures CDockWidget::features() const
{
return d->Features;
}
} // namespace ads
//---------------------------------------------------------------------------

View File

@ -34,8 +34,12 @@
#include <QLabel>
#include <QMouseEvent>
#include <QStyle>
#include <QApplication>
#include <iostream>
#include "DockWidget.h"
#include "DockAreaWidget.h"
namespace ads
{
@ -50,6 +54,8 @@ struct DockWidgetTitleBarPrivate
QLabel* TitleLabel;
QPoint DragStartMousePosition;
bool IsActiveTab = false;
bool TabMoving = false;
CDockAreaWidget* DockArea = nullptr;
/**
* Private data constructor
@ -60,6 +66,11 @@ struct DockWidgetTitleBarPrivate
* Creates the complete layout including all controls
*/
void createLayout();
/**
* Moves the tab depending on the position in the given mouse event
*/
void moveTab(QMouseEvent* ev);
};
// struct DockWidgetTitleBarPrivate
@ -90,6 +101,18 @@ void DockWidgetTitleBarPrivate::createLayout()
TitleLabel->setText(DockWidget->windowTitle());
}
//============================================================================
void DockWidgetTitleBarPrivate::moveTab(QMouseEvent* ev)
{
ev->accept();
int left, top, right, bottom;
_this->getContentsMargins(&left, &top, &right, &bottom);
QPoint moveToPos = _this->mapToParent(ev->pos()) - DragStartMousePosition;
moveToPos.setY(0);
_this->move(moveToPos);
_this->raise();
}
//============================================================================
CDockWidgetTitleBar::CDockWidgetTitleBar(CDockWidget* DockWidget, QWidget *parent) :
@ -124,19 +147,57 @@ void CDockWidgetTitleBar::mousePressEvent(QMouseEvent* ev)
//============================================================================
void CDockWidgetTitleBar::mouseReleaseEvent(QMouseEvent* ev)
{
// End of tab moving, change order now
if (d->TabMoving && d->DockArea)
{
// Find tab under mouse
QPoint pos = d->DockArea->mapFromGlobal(ev->globalPos());
int fromIndex = d->DockArea->tabIndex(d->DockWidget);
int toIndex = d->DockArea->indexOfContentByTitlePos(pos, this);
if (-1 == toIndex)
{
toIndex = d->DockArea->count() - 1;
}
std::cout << "Move tab from " << fromIndex << " to " << toIndex << std::endl;
d->DockArea->reorderDockWidget(fromIndex, toIndex);
}
if (!d->DragStartMousePosition.isNull())
{
emit clicked();
}
d->DragStartMousePosition = QPoint();
d->TabMoving = false;
//mcw->m_SectionDropOverlay->hideDropOverlay();
//mcw->hideContainerOverlay();
QFrame::mouseReleaseEvent(ev);
}
//============================================================================
void CDockWidgetTitleBar::mouseMoveEvent(QMouseEvent* ev)
{
if (!(ev->buttons() & Qt::LeftButton))
{
QFrame::mouseMoveEvent(ev);
return;
}
// move tab
if (d->TabMoving)
{
d->moveTab(ev);
}
if ((ev->pos() - d->DragStartMousePosition).manhattanLength() >= QApplication::startDragDistance() // Wait a few pixels before start moving
&& d->DockArea->titleAreaGeometry().contains(d->DockArea->mapFromGlobal(ev->globalPos())))
{
d->TabMoving = true;
return;
}
QFrame::mouseMoveEvent(ev);
}
@ -171,6 +232,13 @@ CDockWidget* CDockWidgetTitleBar::dockWidget() const
{
return d->DockWidget;
}
//============================================================================
void CDockWidgetTitleBar::setDockAreaWidget(CDockAreaWidget* DockArea)
{
d->DockArea = DockArea;
}
} // namespace ads
//---------------------------------------------------------------------------

View File

@ -35,6 +35,7 @@
namespace ads
{
class CDockWidget;
class CDockAreaWidget;
struct DockWidgetTitleBarPrivate;
/**
@ -82,6 +83,12 @@ public:
*/
CDockWidget* dockWidget() const;
/**
* Sets the dock area widget the dockWidget returned by dockWidget()
* function belongs to.
*/
void setDockAreaWidget(CDockAreaWidget* DockArea);
signals:
void activeTabChanged();
void clicked();

View File

@ -92,7 +92,9 @@ void MainWindow::createContent()
m_DockManager->addDockWidget(ads::LeftDockWidgetArea, createLongTextLabelDockWidget(m_DockManager));
m_DockManager->addDockWidget(ads::BottomDockWidgetArea, createFileSystemTreeDockWidget(m_DockManager));
auto DockArea = m_DockManager->addDockWidget(ads::TopDockWidgetArea, createFileSystemTreeDockWidget(m_DockManager));
m_DockManager->addDockWidget(ads::CenterDockWidgetArea, createCalendarDockWidget(m_DockManager), DockArea);
DockWidget = createCalendarDockWidget(m_DockManager);
DockWidget->setFeatures(DockWidget->features().setFlag(ads::CDockWidget::DockWidgetClosable, false));
m_DockManager->addDockWidget(ads::CenterDockWidgetArea, DockWidget, DockArea);
}