Started implementing DockAreaTabBar to improve code, encapsulation and performance

This commit is contained in:
Uwe Kindler 2018-10-10 15:15:59 +02:00
parent 496aec211e
commit 272bbe275e
9 changed files with 447 additions and 92 deletions

View File

@ -13,12 +13,16 @@
#include <QMouseEvent>
#include <QScrollBar>
#include <QDebug>
#include <QBoxLayout>
#include "FloatingDockContainer.h"
#include "DockAreaWidget.h"
#include "DockOverlay.h"
#include "DockManager.h"
#include "DockWidget.h"
#include "DockWidgetTab.h"
#include <iostream>
namespace ads
{
@ -31,6 +35,9 @@ struct DockAreaTabBarPrivate
QPoint DragStartMousePos;
CDockAreaWidget* DockArea;
CFloatingDockContainer* FloatingWidget = nullptr;
QWidget* TabsContainerWidget;
QBoxLayout* TabsLayout;
int CurrentIndex = -1;
/**
* Private data constructor
@ -57,6 +64,16 @@ CDockAreaTabBar::CDockAreaTabBar(CDockAreaWidget* parent) :
setWidgetResizable(true);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
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);
}
//============================================================================
@ -173,6 +190,113 @@ void CDockAreaTabBar::startFloating(const QPoint& Pos)
TopLevelDockWidget->emitTopLevelChanged(true);
}
}
//============================================================================
void CDockAreaTabBar::setCurrentIndex(int index)
{
if (index == d->CurrentIndex)
{
return;
}
if (index < 0 || index > (d->TabsLayout->count() - 1))
{
qWarning() << Q_FUNC_INFO << "Invalid index" << index;
return;
}
emit currentChanging(index);
// Set active TAB and update all other tabs to be inactive
for (int i = 0; i < d->TabsLayout->count(); ++i)
{
QLayoutItem* item = d->TabsLayout->itemAt(i);
if (!item->widget())
{
continue;
}
auto TabWidget = dynamic_cast<CDockWidgetTab*>(item->widget());
if (!TabWidget)
{
continue;
}
if (i == index)
{
TabWidget->show();
TabWidget->setActiveTab(true);
ensureWidgetVisible(TabWidget);
}
else
{
TabWidget->setActiveTab(false);
}
}
d->CurrentIndex = index;
emit currentChanged(index);
}
//============================================================================
int CDockAreaTabBar::count() const
{
return d->TabsLayout->count();
}
//===========================================================================
void CDockAreaTabBar::insertTab(int Index, CDockWidgetTab* Tab)
{
d->TabsLayout->insertWidget(Index, Tab);
connect(Tab, SIGNAL(clicked()), this, SLOT(onTabClicked()));
}
//===========================================================================
void CDockAreaTabBar::removeTab(CDockWidgetTab* Tab)
{
d->TabsLayout->removeWidget(Tab);
disconnect(Tab, SIGNAL(clicked()), this, SLOT(onTabClicked()));
}
//===========================================================================
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);
setCurrentIndex(index);
std::cout << "emit tabBarClicked " << index << std::endl;
emit tabBarClicked(index);
}
void CDockAreaTabBar::closeTabe(int Index)
{
}
} // namespace ads
//---------------------------------------------------------------------------

View File

@ -15,6 +15,7 @@
namespace ads
{
class CDockAreaWidget;
class CDockWidgetTab;
struct DockAreaTabBarPrivate;
/**
@ -29,6 +30,9 @@ private:
DockAreaTabBarPrivate* d; ///< private data (pimpl)
friend class DockAreaTabBarPrivate;
private slots:
void onTabClicked();
protected:
virtual void wheelEvent(QWheelEvent* Event) override;
/**
@ -67,6 +71,61 @@ public:
* Virtual Destructor
*/
virtual ~CDockAreaTabBar();
/**
* Inserts the given dock widget tab at the given position
*/
void insertTab(int Index, CDockWidgetTab* Tab);
/**
* Removes the given DockWidgetTab from the tabbar
*/
void removeTab(CDockWidgetTab* Tab);
/**
* Returns the number of tabs in this tabbar
*/
int count() const;
/**
* Returns the current index
*/
int currentIndex() const;
/**
* Returns the current tab
*/
CDockWidgetTab* currentTab() const;
public slots:
/**
* This property sets the index of the tab bar's visible tab
*/
void setCurrentIndex(int Index);
/**
* This function will close the tab given in Index param.
* Closing a tab means, the tab will be hidden, it will not be removed
*/
void closeTabe(int Index);
signals:
/**
* This signal is emitted when the tab bar's current tab is about to be changed. The new
* current has the given index, or -1 if there isn't a new one.
*/
void currentChanging(int Index);
/**
* This signal is emitted when the tab bar's current tab changes. The new
* current has the given index, or -1 if there isn't a new one
*/
void currentChanged(int Index);
/**
* This signal is emitted when user clicks on a tab at an index.
*/
void tabBarClicked(int index);
}; // class CDockAreaTabBar
} // namespace ads
//-----------------------------------------------------------------------------

View File

@ -42,6 +42,8 @@
#include <QSplitter>
#include <QXmlStreamWriter>
#include <QVector>
#include <QList>
#include "DockContainerWidget.h"
#include "DockWidget.h"
@ -51,6 +53,8 @@
#include "DockAreaTabBar.h"
#include "DockSplitter.h"
#include <iostream>
namespace ads
{
@ -60,6 +64,210 @@ static const char* const DOCKWIDGET_PROPERTY = "dockwidget";
static const int APPEND = -1;
/**
* Default stack area layout
*/
class CStackedDockAreaLayout
{
private:
QStackedLayout* Layout;
public:
CStackedDockAreaLayout(QBoxLayout* ParentLayout)
{
Layout = new QStackedLayout();
Layout->setContentsMargins(0, 0, 0, 0);
Layout->setSpacing(0);
Layout->setSizeConstraint(QLayout::SetNoConstraint);
ParentLayout->addLayout(Layout, 1);
}
int count() const
{
return Layout->count();
}
void insertWidget(int index, QWidget* Widget)
{
Layout->insertWidget(index, Widget);
}
void removeWidget(QWidget* Widget)
{
Layout->removeWidget(Widget);
}
void setCurrentIndex(int Index)
{
Layout->setCurrentIndex(Index);
}
int currentIndex() const
{
return Layout->currentIndex();
}
QWidget* currentWidget() const
{
return Layout->currentWidget();
}
bool isEmpty() const
{
return Layout->isEmpty();
}
int indexOf(QWidget* w) const
{
return Layout->indexOf(w);
}
QWidget* widget(int index) const
{
return Layout->widget(index);
}
QRect geometry() const
{
return Layout->geometry();
}
};
/**
* New dock area layout
*/
class CDockAreaLayout
{
private:
QBoxLayout* m_ParentLayout;
QList<QWidget*> m_Widgets;
int m_CurrentIndex = -1;
QWidget* m_CurrentWidget = nullptr;
public:
CDockAreaLayout(QBoxLayout* ParentLayout)
: m_ParentLayout(ParentLayout)
{
}
int count() const
{
return m_Widgets.count();
}
void insertWidget(int index, QWidget* Widget)
{
if (index < 0)
{
index = m_Widgets.count();
}
m_Widgets.insert(index, Widget);
if (m_CurrentIndex < 0)
{
setCurrentIndex(index);
}
else
{
if (index <= m_CurrentIndex )
{
++m_CurrentIndex;
}
}
}
void removeWidget(QWidget* Widget)
{
if (currentWidget() == Widget)
{
auto LayoutItem = m_ParentLayout->takeAt(1);
if (LayoutItem)
{
LayoutItem->widget()->setParent(0);
}
}
m_Widgets.removeOne(Widget);
//setCurrentIndex(0);
}
QWidget* currentWidget() const
{
return m_CurrentWidget;
}
void setCurrentIndex(int index)
{
std::cout << "setCurrentIndex" << std::endl;
QWidget *prev = currentWidget();
QWidget *next = widget(index);
if (!next || (next == prev && !m_CurrentWidget))
{
return;
}
bool reenableUpdates = false;
QWidget *parent = m_ParentLayout->parentWidget();
if (parent && parent->updatesEnabled())
{
reenableUpdates = true;
parent->setUpdatesEnabled(false);
}
std::cout << "m_ParentLayout->addWidget(next)" << std::endl;
auto LayoutItem = m_ParentLayout->takeAt(1);
if (LayoutItem)
{
LayoutItem->widget()->setParent(0);
}
m_ParentLayout->addWidget(next);
if (prev)
{
prev->hide();
}
m_CurrentIndex = index;
m_CurrentWidget = next;
if (reenableUpdates)
{
parent->setUpdatesEnabled(true);
}
}
int currentIndex() const
{
return m_CurrentIndex;
}
bool isEmpty() const
{
return m_Widgets.empty();
}
int indexOf(QWidget* w) const
{
return m_Widgets.indexOf(w);
}
QWidget* widget(int index) const
{
return (index < m_Widgets.size()) ? m_Widgets.at(index) : nullptr;
}
QRect geometry() const
{
return m_Widgets.empty() ? QRect() : currentWidget()->geometry();
}
};
using DockAreaLayout = CDockAreaLayout;
/**
* Private data class of CDockAreaWidget class (pimpl)
*/
@ -69,13 +277,11 @@ struct DockAreaWidgetPrivate
QBoxLayout* Layout;
QFrame* TitleBar;
QBoxLayout* TopLayout;
QStackedLayout* ContentsLayout;
DockAreaLayout* ContentsLayout;
CDockAreaTabBar* TabBar;
QWidget* TabsContainerWidget;
QBoxLayout* TabsLayout;
QPushButton* TabsMenuButton;
QPushButton* CloseButton;
int TabsLayoutInitCount;
//int TabsLayoutInitCount;
CDockManager* DockManager = nullptr;
bool MenuOutdated = true;
@ -167,19 +373,11 @@ void DockAreaWidgetPrivate::createTabBar()
TopLayout->setSpacing(0);
TitleBar->setLayout(TopLayout);
Layout->addWidget(TitleBar);
TitleBar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
TabBar = new CDockAreaTabBar(_this);
TopLayout->addWidget(TabBar, 1);
TabsContainerWidget = new QWidget();
TabsContainerWidget->setObjectName("tabsContainerWidget");
TabBar->setWidget(TabsContainerWidget);
TabsLayout = new QBoxLayout(QBoxLayout::LeftToRight);
TabsLayout->setContentsMargins(0, 0, 0, 0);
TabsLayout->setSpacing(0);
TabsLayout->addStretch(1);
TabsContainerWidget->setLayout(TabsLayout);
_this->connect(TabBar, SIGNAL(tabBarClicked(int)), SLOT(setCurrentIndex(int)));
TabsMenuButton = new QPushButton();
TabsMenuButton->setObjectName("tabsMenuButton");
@ -202,8 +400,6 @@ void DockAreaWidgetPrivate::createTabBar()
CloseButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
TopLayout->addWidget(CloseButton, 0);
_this->connect(CloseButton, SIGNAL(clicked()), SLOT(onCloseButtonClicked()));
TabsLayoutInitCount = TabsLayout->count();
}
@ -283,18 +479,14 @@ CDockAreaWidget::CDockAreaWidget(CDockManager* DockManager, CDockContainerWidget
setLayout(d->Layout);
d->createTabBar();
d->ContentsLayout = new QStackedLayout();
d->ContentsLayout->setContentsMargins(0, 0, 0, 0);
d->ContentsLayout->setSpacing(0);
d->ContentsLayout->setSizeConstraint(QLayout::SetNoConstraint);
d->Layout->addLayout(d->ContentsLayout, 1);
d->ContentsLayout = new DockAreaLayout(d->Layout);
}
//============================================================================
CDockAreaWidget::~CDockAreaWidget()
{
qDebug() << "~CDockAreaWidget()";
delete d->ContentsLayout;
delete d;
}
@ -309,18 +501,7 @@ CDockManager* CDockAreaWidget::dockManager() const
//============================================================================
CDockContainerWidget* CDockAreaWidget::dockContainer() const
{
QWidget* Parent = parentWidget();
while (Parent)
{
CDockContainerWidget* Container = dynamic_cast<CDockContainerWidget*>(Parent);
if (Container)
{
return Container;
}
Parent = Parent->parentWidget();
}
return 0;
return internal::findParent<CDockContainerWidget*>(this);
}
@ -338,10 +519,10 @@ void CDockAreaWidget::insertDockWidget(int index, CDockWidget* DockWidget,
d->ContentsLayout->insertWidget(index, DockWidget);
DockWidget->tabWidget()->setDockAreaWidget(this);
auto TabWidget = DockWidget->tabWidget();
d->TabsLayout->insertWidget(index, TabWidget);
d->TabBar->insertTab(index, TabWidget);
TabWidget->setVisible(!DockWidget->isClosed());
connect(TabWidget, SIGNAL(clicked()), this, SLOT(onDockWidgetTitleClicked()));
DockWidget->setProperty(INDEX_PROPERTY, index);
d->markTabsMenuOutdated();
if (Activate)
{
setCurrentIndex(index);
@ -359,8 +540,7 @@ void CDockAreaWidget::removeDockWidget(CDockWidget* DockWidget)
d->ContentsLayout->removeWidget(DockWidget);
auto TabWidget = DockWidget->tabWidget();
TabWidget->hide();
d->TabsLayout->removeWidget(TabWidget);
disconnect(TabWidget, SIGNAL(clicked()), this, SLOT(onDockWidgetTitleClicked()));
d->TabBar->removeTab(TabWidget);
if (NextOpenDockWidget)
{
setCurrentDockWidget(NextOpenDockWidget);
@ -426,20 +606,6 @@ void CDockAreaWidget::hideAreaIfNoVisibleContent()
}
//============================================================================
void CDockAreaWidget::onDockWidgetTitleClicked()
{
CDockWidgetTab* TitleWidget = qobject_cast<CDockWidgetTab*>(sender());
if (!TitleWidget)
{
return;
}
int index = d->TabsLayout->indexOf(TitleWidget);
setCurrentIndex(index);
}
//============================================================================
void CDockAreaWidget::onCloseButtonClicked()
{
@ -469,45 +635,22 @@ void CDockAreaWidget::setCurrentDockWidget(CDockWidget* DockWidget)
//============================================================================
void CDockAreaWidget::setCurrentIndex(int index)
{
if (index < 0 || index > (d->TabsLayout->count() - 1))
if (index < 0 || index > (d->TabBar->count() - 1))
{
qWarning() << Q_FUNC_INFO << "Invalid index" << index;
return;
}
emit currentChanging(index);
// Set active TAB and update all other tabs to be inactive
for (int i = 0; i < d->TabsLayout->count(); ++i)
{
QLayoutItem* item = d->TabsLayout->itemAt(i);
if (!item->widget())
{
continue;
}
auto TitleWidget = dynamic_cast<CDockWidgetTab*>(item->widget());
if (!TitleWidget)
{
continue;
}
if (i == index)
{
TitleWidget->show();
TitleWidget->setActiveTab(true);
d->TabBar->ensureWidgetVisible(TitleWidget);
auto Features = TitleWidget->dockWidget()->features();
d->TabBar->setCurrentIndex(index);
auto Features = d->TabBar->currentTab()->dockWidget()->features();
d->CloseButton->setVisible(Features.testFlag(CDockWidget::DockWidgetClosable));
}
else
{
TitleWidget->setActiveTab(false);
}
}
if (!dockManager()->isRestoringState())
{
d->ContentsLayout->setCurrentIndex(index);
d->ContentsLayout->currentWidget()->show();
}
emit currentChanged(index);
}
@ -618,7 +761,7 @@ void CDockAreaWidget::reorderDockWidget(int fromIndex, int toIndex)
|| toIndex >= d->ContentsLayout->count() || toIndex < 0 || fromIndex == toIndex)
{
qDebug() << "Invalid index for tab movement" << fromIndex << toIndex;
d->TabsLayout->update();
//d->TabsLayout->update();
return;
}
@ -639,11 +782,14 @@ void CDockAreaWidget::reorderDockWidget(int fromIndex, int toIndex)
// 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;
/*liFrom = d->TabsLayout->takeAt(fromIndex);
d->TabsLayout->insertItem(toIndex, liFrom);*/
//liFrom = d->ContentsLayout->takeAt(fromIndex);
//d->ContentsLayout->insertWidget(toIndex, liFrom->widget());
auto Widget = d->ContentsLayout->widget(fromIndex);
d->ContentsLayout->removeWidget(Widget);
d->ContentsLayout->insertWidget(toIndex, Widget);
//delete liFrom;
}

View File

@ -63,7 +63,6 @@ private:
friend class CDockWidget;
private slots:
void onDockWidgetTitleClicked();
void onTabsMenuActionTriggered(QAction* Action);
void onCloseButtonClicked();
void onTabsMenuAboutToShow();

View File

@ -47,6 +47,9 @@
#include "ads_globals.h"
#include "DockSplitter.h"
#include <QElapsedTimer>
#include <iostream>
namespace ads
{
@ -936,6 +939,9 @@ int CDockContainerWidget::visibleDockAreaCount() const
void CDockContainerWidget::dropFloatingWidget(CFloatingDockContainer* FloatingWidget,
const QPoint& TargetPos)
{
QElapsedTimer Timer;
Timer.start();
qDebug() << "CDockContainerWidget::dropFloatingWidget";
CDockAreaWidget* DockArea = dockAreaAt(TargetPos);
auto dropArea = InvalidDockWidgetArea;
@ -977,6 +983,9 @@ void CDockContainerWidget::dropFloatingWidget(CFloatingDockContainer* FloatingWi
{
TopLevelDockWidget->emitTopLevelChanged(false);
}
std::cout << "CDockContainerWidget::dropFloatingWidget " <<
Timer.restart() << std::endl;
}

View File

@ -33,7 +33,6 @@
#include <algorithm>
#include <QMainWindow>
#include <QList>
#include <QMap>
@ -53,6 +52,8 @@
#include "DockStateSerialization.h"
#include "DockAreaWidget.h"
#include <QElapsedTimer>
#include <iostream>
namespace ads
{
@ -448,6 +449,9 @@ QByteArray CDockManager::saveState(eXmlMode XmlMode, int version) const
//============================================================================
bool CDockManager::restoreState(const QByteArray &state, int version)
{
QElapsedTimer Timer;
Timer.start();
// Prevent multiple calls as long as state is not restore. This may
// happen, if QApplication::processEvents() is called somewhere
if (d->RestoringState)
@ -478,6 +482,7 @@ bool CDockManager::restoreState(const QByteArray &state, int version)
show();
}
std::cout << "CDockManager::restoreState " << Timer.restart() << std::endl;
return Result;
}
@ -635,6 +640,13 @@ void CDockManager::setViewMenuInsertionOrder(eViewMenuInsertionOrder Order)
d->MenuInsertionOrder = Order;
}
//===========================================================================
bool CDockManager::isRestoringState() const
{
return d->RestoringState;
}
} // namespace ads
//---------------------------------------------------------------------------

View File

@ -264,6 +264,12 @@ public:
*/
void setViewMenuInsertionOrder(eViewMenuInsertionOrder Order);
/**
* This function returns true between the restoringState() and
* stateRestored() signals.
*/
bool isRestoringState() const;
public slots:
/**
* Opens the perspective with the given name.

View File

@ -327,7 +327,7 @@ void CDockWidget::setDockManager(CDockManager* DockManager)
//============================================================================
CDockContainerWidget* CDockWidget::dockContainer() const
{
return internal::findParent<CDockContainerWidget*>(this);
return d->DockArea->dockContainer();
}

View File

@ -104,7 +104,7 @@ T findParent(const QWidget* w)
QWidget* parentWidget = w->parentWidget();
while (parentWidget)
{
T ParentImpl = dynamic_cast<T>(parentWidget);
T ParentImpl = qobject_cast<T>(parentWidget);
if (ParentImpl)
{
return ParentImpl;