Improved AutoHideSidebar to provide better resize behavior if parent widget is resized

This commit is contained in:
Uwe Kindler 2022-11-03 11:34:04 +01:00
parent 716207f600
commit 3b97fdc2ce
6 changed files with 204 additions and 82 deletions

View File

@ -746,13 +746,14 @@ void CMainWindow::savePerspective()
//============================================================================
void CMainWindow::onViewToggled(bool Open)
{
Q_UNUSED(Open);
auto DockWidget = qobject_cast<ads::CDockWidget*>(sender());
if (!DockWidget)
{
return;
}
//qDebug() << DockWidget->objectName() << " viewToggled(" << Open << ")";
qDebug() << DockWidget->objectName() << " viewToggled(" << Open << ")";
}

View File

@ -45,6 +45,8 @@
namespace ads
{
class CTabsWidget;
/**
* Private data class of CSideTabBar class (pimpl)
*/
@ -57,6 +59,7 @@ struct AutoHideSideBarPrivate
CAutoHideSideBar* _this;
CDockContainerWidget* ContainerWidget;
CTabsWidget* TabsContainerWidget;
QBoxLayout* TabsLayout;
Qt::Orientation Orientation;
SideBarLocation SideTabArea = SideBarLocation::Left;
@ -68,8 +71,43 @@ struct AutoHideSideBarPrivate
{
return Qt::Horizontal == Orientation;
}
/**
* Called from viewport to forward event handling to this
*/
void handleViewportEvent(QEvent* e);
}; // struct AutoHideSideBarPrivate
/**
* This widget stores the tab buttons
*/
class CTabsWidget : public QWidget
{
public:
using QWidget::QWidget;
using Super = QWidget;
AutoHideSideBarPrivate* EventHandler;
/**
* Returns the size hint as minimum size hint
*/
virtual QSize minimumSizeHint() const override
{
return Super::sizeHint();
}
/**
* Forward event handling to EventHandler
*/
/*virtual bool event(QEvent* e) override
{
EventHandler->handleViewportEvent(e);
return Super::event(e);
}*/
};
//============================================================================
AutoHideSideBarPrivate::AutoHideSideBarPrivate(CAutoHideSideBar* _public) :
_this(_public)
@ -77,6 +115,45 @@ AutoHideSideBarPrivate::AutoHideSideBarPrivate(CAutoHideSideBar* _public) :
}
//============================================================================
void AutoHideSideBarPrivate::handleViewportEvent(QEvent* e)
{
switch (e->type())
{
case QEvent::ChildRemoved:
if (TabsLayout->isEmpty())
{
_this->hide();
}
break;
case QEvent::Resize:
if (_this->tabCount())
{
auto ev = static_cast<QResizeEvent*>(e);
auto Tab = _this->tabAt(0);
int Size = isHorizontal() ? ev->size().height() : ev->size().width();
int TabSize = isHorizontal() ? Tab->size().height() : Tab->size().width();
// If the size of the side bar is less than the size of the first tab
// then there are no visible tabs in this side bar. This check will
// fail if someone will force a very big border via CSS!!
if (Size < TabSize)
{
_this->hide();
}
}
else
{
_this->hide();
}
break;
default:
break;
}
}
//============================================================================
CAutoHideSideBar::CAutoHideSideBar(CDockContainerWidget* parent, SideBarLocation area) :
Super(parent),
@ -87,16 +164,24 @@ CAutoHideSideBar::CAutoHideSideBar(CDockContainerWidget* parent, SideBarLocation
d->Orientation = (area == SideBarLocation::Bottom || area == SideBarLocation::Top)
? Qt::Horizontal : Qt::Vertical;
auto mainLayout = new QBoxLayout(d->Orientation == Qt::Vertical ? QBoxLayout::TopToBottom : QBoxLayout::LeftToRight);
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
setFrameStyle(QFrame::NoFrame);
setWidgetResizable(true);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
d->TabsContainerWidget = new CTabsWidget();
d->TabsContainerWidget->EventHandler = d;
d->TabsContainerWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
d->TabsContainerWidget->setObjectName("sideTabsContainerWidget");
d->TabsLayout = new QBoxLayout(d->Orientation == Qt::Vertical ? QBoxLayout::TopToBottom : QBoxLayout::LeftToRight);
d->TabsLayout->setContentsMargins(0, 0, 0, 0);
d->TabsLayout->setSpacing(0);
mainLayout->addLayout(d->TabsLayout);
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSpacing(0);
mainLayout->addStretch(1);
setLayout(mainLayout);
d->TabsLayout->setSpacing(12);
d->TabsLayout->addStretch(1);
d->TabsContainerWidget->setLayout(d->TabsLayout);
setWidget(d->TabsContainerWidget);
setFocusPolicy(Qt::NoFocus);
if (d->isHorizontal())
@ -108,6 +193,8 @@ CAutoHideSideBar::CAutoHideSideBar(CDockContainerWidget* parent, SideBarLocation
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
}
qDebug() << "d->TabsLayout->count " << d->TabsLayout->count();
hide();
}
@ -130,9 +217,16 @@ CAutoHideSideBar::~CAutoHideSideBar()
//============================================================================
void CAutoHideSideBar::insertTab(int Index, CAutoHideTab* SideTab)
{
SideTab->installEventFilter(this);
SideTab->setSideBar(this);
d->TabsLayout->insertWidget(Index, SideTab);
SideTab->installEventFilter(this);
if (Index < 0)
{
d->TabsLayout->insertWidget(d->TabsLayout->count() - 1, SideTab);
}
else
{
d->TabsLayout->insertWidget(Index, SideTab);
}
show();
}
@ -161,46 +255,6 @@ void CAutoHideSideBar::removeTab(CAutoHideTab* SideTab)
}
//============================================================================
bool CAutoHideSideBar::event(QEvent* e)
{
switch (e->type())
{
case QEvent::ChildRemoved:
if (d->TabsLayout->isEmpty())
{
hide();
}
break;
case QEvent::Resize:
if (d->TabsLayout->count())
{
auto ev = static_cast<QResizeEvent*>(e);
auto Tab = tabAt(0);
int Size = d->isHorizontal() ? ev->size().height() : ev->size().width();
int TabSize = d->isHorizontal() ? Tab->size().height() : Tab->size().width();
// If the size of the side bar is less than the size of the first tab
// then there are no visible tabs in this side bar. This check will
// fail if someone will force a very big border via CSS!!
if (Size < TabSize)
{
hide();
}
}
else
{
hide();
}
break;
default:
break;
}
return Super::event(e);
}
//============================================================================
bool CAutoHideSideBar::eventFilter(QObject *watched, QEvent *event)
{
@ -209,7 +263,7 @@ bool CAutoHideSideBar::eventFilter(QObject *watched, QEvent *event)
return false;
}
// As soon as on tab is shhown, we need to show the side tab bar
// As soon as on tab is shown, we need to show the side tab bar
auto Tab = qobject_cast<CAutoHideTab*>(watched);
if (Tab)
{
@ -235,7 +289,7 @@ CAutoHideTab* CAutoHideSideBar::tabAt(int index) const
//============================================================================
int CAutoHideSideBar::tabCount() const
{
return d->TabsLayout->count();
return d->TabsLayout->count() - 1;
}
@ -272,5 +326,33 @@ void CAutoHideSideBar::saveState(QXmlStreamWriter& s) const
s.writeEndElement();
}
//===========================================================================
QSize CAutoHideSideBar::minimumSizeHint() const
{
QSize Size = sizeHint();
Size.setWidth(10);
return Size;
}
//===========================================================================
QSize CAutoHideSideBar::sizeHint() const
{
return d->TabsContainerWidget->sizeHint();
}
//===========================================================================
int CAutoHideSideBar::spacing() const
{
return d->TabsLayout->spacing();
}
//===========================================================================
void CAutoHideSideBar::setSpacing(int Spacing)
{
d->TabsLayout->setSpacing(Spacing);
}
} // namespace ads

View File

@ -29,7 +29,7 @@
//============================================================================
// INCLUDES
//============================================================================
#include <QFrame>
#include <QScrollArea>
#include "ads_globals.h"
#include "AutoHideTab.h"
@ -50,12 +50,16 @@ class CDockingStateReader;
* it contains visible tabs. If it is empty or all tabs are hidden, then the
* side bar is also hidden. As soon as one single tab becomes visible, this
* tab bar will be shown.
* The CAutoHideSideBar uses a QScrollArea here, to enable proper resizing.
* If the side bar contains many tabs, then the tabs are simply clipped - this
* is the same like in visual studio
*/
class ADS_EXPORT CAutoHideSideBar : public QFrame
class ADS_EXPORT CAutoHideSideBar : public QScrollArea
{
Q_OBJECT
Q_PROPERTY(int sideBarLocation READ sideBarLocation)
Q_PROPERTY(Qt::Orientation orientation READ orientation)
Q_PROPERTY(int spacing READ spacing WRITE setSpacing)
private:
AutoHideSideBarPrivate* d; ///< private data (pimpl)
@ -64,7 +68,6 @@ private:
friend DockContainerWidgetPrivate;
protected:
virtual bool event(QEvent* e) override;
virtual bool eventFilter(QObject *watched, QEvent *event) override;
/**
@ -79,7 +82,7 @@ protected:
void insertTab(int Index, CAutoHideTab* SideTab);
public:
using Super = QFrame;
using Super = QScrollArea;
/**
* Default Constructor
@ -123,6 +126,31 @@ public:
*/
SideBarLocation sideBarLocation() const;
/**
* Overrides the minimumSizeHint() function of QScrollArea
* The minimumSizeHint() is bigger than the sizeHint () for the scroll
* area because even if the scrollbars are invisible, the required speace
* is reserved in the minimumSizeHint(). This override simply returns
* sizeHint();
*/
virtual QSize minimumSizeHint() const override;
/**
* The function provides a sizeHint that matches the height of the
* internal viewport.
*/
virtual QSize sizeHint() const override;
/**
* Getter for spacing property - returns the spacing of the tabs
*/
int spacing() const;
/**
* Setter for spacing property - sets the spacing
*/
void setSpacing(int Spacing);
Q_SIGNALS:
void sideTabAutoHideToggleRequested();
};

View File

@ -173,6 +173,14 @@ SideBarLocation CAutoHideTab::sideBarLocation() const
void CAutoHideTab::setOrientation(Qt::Orientation Orientation)
{
d->Orientation = Orientation;
if (orientation() == Qt::Horizontal)
{
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
}
else
{
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
}
CPushButton::setButtonOrientation((Qt::Horizontal == Orientation)
? CPushButton::Horizontal : CPushButton::VerticalTopToBottom);
updateStyle();

View File

@ -131,53 +131,52 @@ QScrollArea#dockWidgetScrollArea {
* CAutoHideTab
*****************************************************************************/
ads--CAutoHideTab {
qproperty-iconSize: 16px 16px;/* this is optional in case you would like to change icon size*/
qproperty-iconSize: 16px 16px;/* this is optional in case you would like to change icon size*/
background: none;
border: none;
padding-left: 2px;
padding-right: 0px;
text-align: center;
margin-right: 6px;
min-height: 20;
min-height: 20px;
padding-bottom: 2px;
}
ads--CAutoHideTab[sideBarLocation="0"],
ads--CAutoHideTab[sideBarLocation="2"] {
border-top: 5px solid rgba(0, 0, 0, 48);
border-top: 6px solid rgba(0, 0, 0, 48);
}
ads--CAutoHideTab[sideBarLocation="1"],
ads--CAutoHideTab[sideBarLocation="3"] {
border-bottom: 5px solid rgba(0, 0, 0, 48);
border-bottom: 6px solid rgba(0, 0, 0, 48);
}
ads--CAutoHideTab:hover[sideBarLocation="0"],
ads--CAutoHideTab:hover[sideBarLocation="2"] {
border-top: 5px solid palette(highlight);
border-top: 6px solid palette(highlight);
color: palette(highlight);
}
ads--CAutoHideTab:hover[sideBarLocation="1"],
ads--CAutoHideTab:hover[sideBarLocation="3"] {
border-bottom: 5px solid palette(highlight);
border-bottom: 6px solid palette(highlight);
color: palette(highlight);
}
ads--CAutoHideTab[sideBarLocation="0"][activeTab="true"],
ads--CAutoHideTab[sideBarLocation="2"][activeTab="true"] {
border-top: 5px solid palette(highlight);
border-top: 6px solid palette(highlight);
}
ads--CAutoHideTab[sideBarLocation="1"][activeTab="true"],
ads--CAutoHideTab[sideBarLocation="3"][activeTab="true"] {
border-bottom: 5px solid palette(highlight);
border-bottom: 6px solid palette(highlight);
}
@ -186,6 +185,12 @@ ads--CAutoHideTab[sideBarLocation="3"][activeTab="true"] {
*****************************************************************************/
ads--CAutoHideSideBar{
background: palette(window);
border: none;
qproperty-spacing: 12;
}
#sideTabsContainerWidget {
background: transparent;
}

View File

@ -168,59 +168,52 @@ QScrollArea#dockWidgetScrollArea {
* CAutoHideTab
*****************************************************************************/
ads--CAutoHideTab {
/*background: palette(window);*/
qproperty-iconSize: 16px 16px;/* this is optional in case you would like to change icon size*/
}
ads--CAutoHideTab {
background: none;
border: none;
padding-left: 2px;
padding-right: 0px;
text-align: center;
min-height: 20px;
padding-bottom: 2px;
}
ads--CAutoHideTab[sideBarLocation="0"],
ads--CAutoHideTab[sideBarLocation="2"] {
border-top: 5px solid rgba(0, 0, 0, 48);
margin-right: 6px;
min-height: 20;
border-top: 6px solid rgba(0, 0, 0, 48);
}
ads--CAutoHideTab[sideBarLocation="1"],
ads--CAutoHideTab[sideBarLocation="3"] {
border-bottom: 5px solid rgba(0, 0, 0, 48);
margin-right: 6px;
min-height: 20;
border-bottom: 6px solid rgba(0, 0, 0, 48);
}
ads--CAutoHideTab:hover[sideBarLocation="0"],
ads--CAutoHideTab:hover[sideBarLocation="2"] {
border-top: 5px solid palette(highlight);
border-top: 6px solid palette(highlight);
color: palette(highlight);
}
ads--CAutoHideTab:hover[sideBarLocation="1"],
ads--CAutoHideTab:hover[sideBarLocation="3"] {
border-bottom: 5px solid palette(highlight);
border-bottom: 6px solid palette(highlight);
color: palette(highlight);
}
ads--CAutoHideTab[sideBarLocation="0"][activeTab="true"],
ads--CAutoHideTab[sideBarLocation="2"][activeTab="true"] {
border-top: 5px solid palette(highlight);
border-top: 6px solid palette(highlight);
}
ads--CAutoHideTab[sideBarLocation="1"][activeTab="true"],
ads--CAutoHideTab[sideBarLocation="3"][activeTab="true"] {
border-bottom: 5px solid palette(highlight);
border-bottom: 6px solid palette(highlight);
}
@ -229,6 +222,12 @@ ads--CAutoHideTab[sideBarLocation="3"][activeTab="true"] {
*****************************************************************************/
ads--CAutoHideSideBar{
background: palette(window);
border: none;
qproperty-spacing: 12;
}
#sideTabsContainerWidget {
background: transparent;
}
@ -336,4 +335,3 @@ ads--CAutoHideDockContainer[sideBarLocation="2"] ads--CResizeHandle {
ads--CAutoHideDockContainer[sideBarLocation="3"] ads--CResizeHandle {
border-top: 1px solid palette(dark);
}