From 3b97fdc2ceff71d814847e9f72df6183ec87cc0b Mon Sep 17 00:00:00 2001 From: Uwe Kindler Date: Thu, 3 Nov 2022 11:34:04 +0100 Subject: [PATCH] Improved AutoHideSidebar to provide better resize behavior if parent widget is resized --- demo/MainWindow.cpp | 3 +- src/AutoHideSideBar.cpp | 184 ++++++++++++++++++------- src/AutoHideSideBar.h | 36 ++++- src/AutoHideTab.cpp | 8 ++ src/stylesheets/default.css | 25 ++-- src/stylesheets/focus_highlighting.css | 30 ++-- 6 files changed, 204 insertions(+), 82 deletions(-) diff --git a/demo/MainWindow.cpp b/demo/MainWindow.cpp index e0394c4..e46bd7c 100644 --- a/demo/MainWindow.cpp +++ b/demo/MainWindow.cpp @@ -746,13 +746,14 @@ void CMainWindow::savePerspective() //============================================================================ void CMainWindow::onViewToggled(bool Open) { + Q_UNUSED(Open); auto DockWidget = qobject_cast(sender()); if (!DockWidget) { return; } - //qDebug() << DockWidget->objectName() << " viewToggled(" << Open << ")"; + qDebug() << DockWidget->objectName() << " viewToggled(" << Open << ")"; } diff --git a/src/AutoHideSideBar.cpp b/src/AutoHideSideBar.cpp index 17d25c8..23babba 100644 --- a/src/AutoHideSideBar.cpp +++ b/src/AutoHideSideBar.cpp @@ -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(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(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(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 + diff --git a/src/AutoHideSideBar.h b/src/AutoHideSideBar.h index 11f7345..3e57e2b 100644 --- a/src/AutoHideSideBar.h +++ b/src/AutoHideSideBar.h @@ -29,7 +29,7 @@ //============================================================================ // INCLUDES //============================================================================ -#include +#include #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(); }; diff --git a/src/AutoHideTab.cpp b/src/AutoHideTab.cpp index 461085d..9377980 100644 --- a/src/AutoHideTab.cpp +++ b/src/AutoHideTab.cpp @@ -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(); diff --git a/src/stylesheets/default.css b/src/stylesheets/default.css index 155de9e..718b482 100644 --- a/src/stylesheets/default.css +++ b/src/stylesheets/default.css @@ -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; } diff --git a/src/stylesheets/focus_highlighting.css b/src/stylesheets/focus_highlighting.css index 1e09c43..1057769 100644 --- a/src/stylesheets/focus_highlighting.css +++ b/src/stylesheets/focus_highlighting.css @@ -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); } -