makes tabs scrollable
scrollable by mouse wheel
adds menu to access not visible tabs
This commit is contained in:
mfreiholz 2016-05-23 07:47:28 +02:00
parent 5c9b383af8
commit 4c65bde599
4 changed files with 161 additions and 24 deletions

View File

@ -5,8 +5,11 @@
#include <QPointer> #include <QPointer>
#include <QList> #include <QList>
#include <QFrame> #include <QFrame>
#include <QScrollArea>
class QBoxLayout; class QBoxLayout;
class QStackedLayout; class QStackedLayout;
class QPushButton;
class QMenu;
#include "ads/API.h" #include "ads/API.h"
#include "ads/Internal.h" #include "ads/Internal.h"
@ -41,17 +44,24 @@ public:
void addContent(const InternalContentData& data, bool autoActivate); void addContent(const InternalContentData& data, bool autoActivate);
bool takeContent(int uid, InternalContentData& data); bool takeContent(int uid, InternalContentData& data);
int indexOfContent(const SectionContent::RefPtr& c) const; int indexOfContent(const SectionContent::RefPtr& c) const;
int indexOfContentByUid(int uid) const;
int indexOfContentByTitlePos(const QPoint& pos, QWidget* exclude = NULL) const; int indexOfContentByTitlePos(const QPoint& pos, QWidget* exclude = NULL) const;
int currentIndex() const; int currentIndex() const;
void moveContent(int from, int to); void moveContent(int from, int to);
protected:
virtual void showEvent(QShowEvent*);
public slots: public slots:
void setCurrentIndex(int index); void setCurrentIndex(int index);
private slots: private slots:
void onSectionTitleClicked(); void onSectionTitleClicked();
void onCloseButtonClicked(); void onCloseButtonClicked();
void onTabsMenuActionTriggered(bool);
void updateTabsMenu();
private: private:
const int _uid; const int _uid;
@ -61,7 +71,13 @@ private:
QList<SectionTitleWidget*> _sectionTitles; QList<SectionTitleWidget*> _sectionTitles;
QList<SectionContentWidget*> _sectionContents; QList<SectionContentWidget*> _sectionContents;
QBoxLayout* _topLayout;
QScrollArea* _tabsScrollArea;
QWidget* _tabsContainerWidget;
QBoxLayout* _tabsLayout; QBoxLayout* _tabsLayout;
QPushButton* _tabsMenuButton;
int _tabsLayoutInitCount; // used for calculations on _tabsLayout modification calls.
QStackedLayout *_contentsLayout; QStackedLayout *_contentsLayout;
QPoint _mousePressPoint; QPoint _mousePressPoint;
@ -71,5 +87,16 @@ private:
static int GetNextUid(); static int GetNextUid();
}; };
/* Custom scrollable implementation for tabs */
class SectionWidgetTabsScrollArea : public QScrollArea
{
public:
SectionWidgetTabsScrollArea(SectionWidget* sectionWidget, QWidget* parent = NULL);
virtual ~SectionWidgetTabsScrollArea();
protected:
virtual void wheelEvent(QWheelEvent*);
};
ADS_NAMESPACE_END ADS_NAMESPACE_END
#endif #endif

View File

@ -22,6 +22,12 @@ SectionWidget
border: 1px solid palette(light); border: 1px solid palette(light);
} }
ads--SectionWidget #tabsMenuButton::menu-indicator,
SectionWidget #tabsMenuButton::menu-indicator
{
image: none;
}
ads--SectionTitleWidget, ads--SectionTitleWidget,
SectionTitleWidget SectionTitleWidget
{ {

View File

@ -162,7 +162,6 @@ void SectionTitleWidget::mouseReleaseEvent(QMouseEvent* ev)
const int fromIndex = section->indexOfContent(_content); const int fromIndex = section->indexOfContent(_content);
const int toIndex = section->indexOfContentByTitlePos(pos, this); const int toIndex = section->indexOfContentByTitlePos(pos, this);
section->moveContent(fromIndex, toIndex); section->moveContent(fromIndex, toIndex);
section->layout()->update();
} }
if (!_dragStartPos.isNull()) if (!_dragStartPos.isNull())

View File

@ -4,12 +4,15 @@
#include <QBoxLayout> #include <QBoxLayout>
#include <QStackedLayout> #include <QStackedLayout>
#include <QMouseEvent> #include <QMouseEvent>
#include <QWheelEvent>
#include <QDragEnterEvent> #include <QDragEnterEvent>
#include <QMimeData> #include <QMimeData>
#include <QPainter> #include <QPainter>
#include <QStyle> #include <QStyle>
#include <QSplitter> #include <QSplitter>
#include <QPushButton> #include <QPushButton>
#include <QScrollBar>
#include <QMenu>
#if defined(ADS_ANIMATIONS_ENABLED) #if defined(ADS_ANIMATIONS_ENABLED)
#include <QGraphicsDropShadowEffect> #include <QGraphicsDropShadowEffect>
@ -25,15 +28,12 @@
ADS_NAMESPACE_BEGIN ADS_NAMESPACE_BEGIN
//int SectionWidget::NextUid = 1;
//QHash<int, SectionWidget*> SectionWidget::LookupMap;
//QHash<ContainerWidget*, QHash<int, SectionWidget*> > SectionWidget::LookupMapByContainer;
SectionWidget::SectionWidget(ContainerWidget* parent) : SectionWidget::SectionWidget(ContainerWidget* parent) :
QFrame(parent), QFrame(parent),
_uid(GetNextUid()), _uid(GetNextUid()),
_container(parent), _container(parent),
_tabsLayout(NULL), _tabsLayout(NULL),
_tabsLayoutInitCount(0),
_contentsLayout(NULL), _contentsLayout(NULL),
_mousePressTitleWidget(NULL) _mousePressTitleWidget(NULL)
{ {
@ -42,11 +42,36 @@ SectionWidget::SectionWidget(ContainerWidget* parent) :
l->setSpacing(0); l->setSpacing(0);
setLayout(l); setLayout(l);
/* top area with tabs and close button */
_topLayout = new QBoxLayout(QBoxLayout::LeftToRight);
_topLayout->setContentsMargins(0, 0, 0, 0);
_topLayout->setSpacing(0);
l->addLayout(_topLayout);
_tabsScrollArea = new SectionWidgetTabsScrollArea(this);
_topLayout->addWidget(_tabsScrollArea, 1);
_tabsContainerWidget = new QWidget();
_tabsScrollArea->setWidget(_tabsContainerWidget);
_tabsLayout = new QBoxLayout(QBoxLayout::LeftToRight); _tabsLayout = new QBoxLayout(QBoxLayout::LeftToRight);
_tabsLayout->setContentsMargins(0, 0, 0, 0); _tabsLayout->setContentsMargins(0, 0, 0, 0);
_tabsLayout->setSpacing(0); _tabsLayout->setSpacing(0);
_tabsLayout->addStretch(1); _tabsLayout->addStretch(1);
l->addLayout(_tabsLayout); _tabsContainerWidget->setLayout(_tabsLayout);
_tabsMenuButton = new QPushButton();
_tabsMenuButton->setObjectName("tabsMenuButton");
_tabsMenuButton->setFlat(true);
_tabsMenuButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarUnshadeButton));
_tabsMenuButton->setMaximumWidth(_tabsMenuButton->iconSize().width());
_topLayout->addWidget(_tabsMenuButton, 0);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
//QObject::connect(_tabsMenuButton, &QPushButton::clicked, this, &SectionWidget::onTabsMenuButtonClicked);
#else
//QObject::connect(_tabsMenuButton, SIGNAL(clicked()), this, SLOT(onTabsMenuButtonClicked()));
#endif
QPushButton* closeButton = new QPushButton(); QPushButton* closeButton = new QPushButton();
closeButton->setObjectName("closeButton"); closeButton->setObjectName("closeButton");
@ -54,13 +79,17 @@ SectionWidget::SectionWidget(ContainerWidget* parent) :
closeButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarCloseButton)); closeButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarCloseButton));
closeButton->setToolTip(tr("Close")); closeButton->setToolTip(tr("Close"));
closeButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); closeButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
_tabsLayout->addWidget(closeButton); _topLayout->addWidget(closeButton, 0);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
QObject::connect(closeButton, &QPushButton::clicked, this, &SectionWidget::onCloseButtonClicked); QObject::connect(closeButton, &QPushButton::clicked, this, &SectionWidget::onCloseButtonClicked);
#else #else
QObject::connect(closeButton, SIGNAL(clicked(bool)), this, SLOT(onCloseButtonClicked())); QObject::connect(closeButton, SIGNAL(clicked(bool)), this, SLOT(onCloseButtonClicked()));
#endif #endif
_tabsLayoutInitCount = _tabsLayout->count();
/* central area with contents */
_contentsLayout = new QStackedLayout(); _contentsLayout = new QStackedLayout();
_contentsLayout->setContentsMargins(0, 0, 0, 0); _contentsLayout->setContentsMargins(0, 0, 0, 0);
_contentsLayout->setSpacing(0); _contentsLayout->setSpacing(0);
@ -104,7 +133,7 @@ ContainerWidget* SectionWidget::containerWidget() const
QRect SectionWidget::titleAreaGeometry() const QRect SectionWidget::titleAreaGeometry() const
{ {
return _tabsLayout->geometry(); return _topLayout->geometry();
} }
QRect SectionWidget::contentAreaGeometry() const QRect SectionWidget::contentAreaGeometry() const
@ -118,7 +147,7 @@ void SectionWidget::addContent(const SectionContent::RefPtr& c)
SectionTitleWidget* title = new SectionTitleWidget(c, NULL); SectionTitleWidget* title = new SectionTitleWidget(c, NULL);
_sectionTitles.append(title); _sectionTitles.append(title);
_tabsLayout->insertWidget(_tabsLayout->count() - 2, title); _tabsLayout->insertWidget(_tabsLayout->count() - _tabsLayoutInitCount, title);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
QObject::connect(title, &SectionTitleWidget::clicked, this, &SectionWidget::onSectionTitleClicked); QObject::connect(title, &SectionTitleWidget::clicked, this, &SectionWidget::onSectionTitleClicked);
#else #else
@ -135,6 +164,8 @@ void SectionWidget::addContent(const SectionContent::RefPtr& c)
// Switch to newest. // Switch to newest.
// else // else
// setCurrentIndex(_contentsLayout->count() - 1); // setCurrentIndex(_contentsLayout->count() - 1);
updateTabsMenu();
} }
void SectionWidget::addContent(const InternalContentData& data, bool autoActivate) void SectionWidget::addContent(const InternalContentData& data, bool autoActivate)
@ -144,7 +175,7 @@ void SectionWidget::addContent(const InternalContentData& data, bool autoActivat
// Add title-widget to tab-bar // Add title-widget to tab-bar
// #FIX: Make it visible, since it is possible that it was hidden previously. // #FIX: Make it visible, since it is possible that it was hidden previously.
_sectionTitles.append(data.titleWidget); _sectionTitles.append(data.titleWidget);
_tabsLayout->insertWidget(_tabsLayout->count() - 2, data.titleWidget); _tabsLayout->insertWidget(_tabsLayout->count() - _tabsLayoutInitCount, data.titleWidget);
data.titleWidget->setVisible(true); data.titleWidget->setVisible(true);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
QObject::connect(data.titleWidget, &SectionTitleWidget::clicked, this, &SectionWidget::onSectionTitleClicked); QObject::connect(data.titleWidget, &SectionTitleWidget::clicked, this, &SectionWidget::onSectionTitleClicked);
@ -166,6 +197,8 @@ void SectionWidget::addContent(const InternalContentData& data, bool autoActivat
// Mark it as inactive tab. // Mark it as inactive tab.
else else
data.titleWidget->setActiveTab(false); // or: setCurrentIndex(currentIndex()) data.titleWidget->setActiveTab(false); // or: setCurrentIndex(currentIndex())
updateTabsMenu();
} }
bool SectionWidget::takeContent(int uid, InternalContentData& data) bool SectionWidget::takeContent(int uid, InternalContentData& data)
@ -217,6 +250,8 @@ bool SectionWidget::takeContent(int uid, InternalContentData& data)
setCurrentIndex(0); setCurrentIndex(0);
} }
updateTabsMenu();
data.content = sc; data.content = sc;
data.titleWidget = title; data.titleWidget = title;
data.contentWidget = content; data.contentWidget = content;
@ -228,6 +263,16 @@ int SectionWidget::indexOfContent(const SectionContent::RefPtr& c) const
return _contents.indexOf(c); return _contents.indexOf(c);
} }
int SectionWidget::indexOfContentByUid(int uid) const
{
for (int i = 0; i < _contents.count(); ++i)
{
if (_contents[i]->uid() == uid)
return i;
}
return -1;
}
int SectionWidget::indexOfContentByTitlePos(const QPoint& p, QWidget* exclude) const int SectionWidget::indexOfContentByTitlePos(const QPoint& p, QWidget* exclude) const
{ {
int index = -1; int index = -1;
@ -251,17 +296,16 @@ void SectionWidget::moveContent(int from, int to)
{ {
if (from >= _contents.size() || from < 0 || to >= _contents.size() || to < 0 || from == to) if (from >= _contents.size() || from < 0 || to >= _contents.size() || to < 0 || from == to)
{ {
qCritical() << "Invalid index for tab movement" << from << to; qDebug() << "Invalid index for tab movement" << from << to;
_tabsLayout->update();
return; return;
} }
SectionContent::RefPtr sc = _contents.at(from);
_contents.move(from, to); _contents.move(from, to);
_sectionTitles.move(from, to); _sectionTitles.move(from, to);
_sectionContents.move(from, to); _sectionContents.move(from, to);
QLayoutItem* liFrom = NULL; QLayoutItem* liFrom = NULL;
liFrom = _tabsLayout->takeAt(from); liFrom = _tabsLayout->takeAt(from);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
_tabsLayout->insertItem(to, liFrom); _tabsLayout->insertItem(to, liFrom);
@ -274,6 +318,13 @@ void SectionWidget::moveContent(int from, int to)
liFrom = _contentsLayout->takeAt(from); liFrom = _contentsLayout->takeAt(from);
_contentsLayout->insertWidget(to, liFrom->widget()); _contentsLayout->insertWidget(to, liFrom->widget());
delete liFrom; delete liFrom;
updateTabsMenu();
}
void SectionWidget::showEvent(QShowEvent*)
{
_tabsScrollArea->ensureWidgetVisible(_sectionTitles.at(currentIndex()));
} }
void SectionWidget::setCurrentIndex(int index) void SectionWidget::setCurrentIndex(int index)
@ -294,7 +345,10 @@ void SectionWidget::setCurrentIndex(int index)
if (stw) if (stw)
{ {
if (i == index) if (i == index)
{
stw->setActiveTab(true); stw->setActiveTab(true);
_tabsScrollArea->ensureWidgetVisible(stw);
}
else else
stw->setActiveTab(false); stw->setActiveTab(false);
} }
@ -326,23 +380,74 @@ void SectionWidget::onCloseButtonClicked()
_container->hideSectionContent(sc); _container->hideSectionContent(sc);
} }
void SectionWidget::onTabsMenuActionTriggered(bool)
{
QAction* a = qobject_cast<QAction*>(sender());
if (a)
{
const int uid = a->data().toInt();
const int index = indexOfContentByUid(uid);
if (index >= 0)
setCurrentIndex(index);
}
}
void SectionWidget::updateTabsMenu()
{
QMenu* m = new QMenu();
for (int i = 0; i < _contents.count(); ++i)
{
const SectionContent::RefPtr& sc = _contents.at(i);
QAction* a = m->addAction(QIcon(), sc->visibleTitle());
a->setData(sc->uid());
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
QObject::connect(a, &QAction::triggered, this, &SectionWidget::onTabsMenuActionTriggered);
#else
QObject::connect(a, SIGNAL(triggered(bool)), this, SLOT(onTabsMenuActionTriggered(bool)));
#endif
}
QMenu* old = _tabsMenuButton->menu();
_tabsMenuButton->setMenu(m);
delete old;
}
int SectionWidget::GetNextUid() int SectionWidget::GetNextUid()
{ {
static int NextUid = 0; static int NextUid = 0;
return ++NextUid; return ++NextUid;
} }
//QHash<int, SectionWidget*>& SectionWidget::GetLookupMap() /*****************************************************************************/
//{
// static QHash<int, SectionWidget*> LookupMap;
// return LookupMap;
//} SectionWidgetTabsScrollArea::SectionWidgetTabsScrollArea(SectionWidget*,
QWidget* parent) :
QScrollArea(parent)
{
/* Important: QSizePolicy::Ignored makes the QScrollArea behaves
like a QLabel and automatically fits into the layout. */
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Ignored);
setFrameStyle(QFrame::NoFrame);
setWidgetResizable(true);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}
//QHash<ContainerWidget*, QHash<int, SectionWidget*> >& SectionWidget::GetLookupMapByContainer() SectionWidgetTabsScrollArea::~SectionWidgetTabsScrollArea()
//{ {
// static QHash<ContainerWidget*, QHash<int, SectionWidget*> > LookupMapByContainer; }
// return LookupMapByContainer;
//} void SectionWidgetTabsScrollArea::wheelEvent(QWheelEvent* e)
{
e->accept();
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
const int direction = e->angleDelta().y();
#else
const int direction = e->delta();
#endif
if (direction < 0)
horizontalScrollBar()->setValue(horizontalScrollBar()->value() + 20);
else
horizontalScrollBar()->setValue(horizontalScrollBar()->value() - 20);
}
ADS_NAMESPACE_END ADS_NAMESPACE_END