Add Bottom side bar

This commit is contained in:
Syarif Fakhri 2022-09-13 10:42:58 +08:00
parent 489f72aa0c
commit 214c415fa2
10 changed files with 289 additions and 71 deletions

View File

@ -1444,6 +1444,7 @@ CDockContainerWidget::CDockContainerWidget(CDockManager* DockManager, QWidget *p
d->Layout->setContentsMargins(0, 0, 0, 0);
d->Layout->setSpacing(0);
d->Layout->setColumnStretch(1, 1);
d->Layout->setRowStretch(0, 1);
setLayout(d->Layout);
// The function d->newSplitter() accesses the config flags from dock
@ -1524,8 +1525,24 @@ COverlayDockContainer* CDockContainerWidget::createAndInitializeDockWidgetOverla
//============================================================================
CDockWidgetSideTab::SideTabBarArea CDockContainerWidget::getDockAreaPosition(CDockAreaWidget* DockAreaWidget)
{
const auto dockWidgetCenter = DockAreaWidget->mapToGlobal(DockAreaWidget->frameGeometry().center());
// Handle bottom case
// It's bottom if the width is wider than the height, and if it's below 50% of the window
const auto dockWidgetFrameGeometry = DockAreaWidget->frameGeometry();
const auto splitterCenter = rootSplitter()->mapToGlobal(rootSplitter()->frameGeometry().center());
const auto a = dockWidgetFrameGeometry.width();
const auto b = dockWidgetFrameGeometry.height();
const auto c = DockAreaWidget->mapToGlobal(dockWidgetFrameGeometry.topLeft()).y();
const auto d = splitterCenter.y();
const auto e = CDockManager::testConfigFlag(CDockManager::DockContainerHasBottomSideBar);
if (dockWidgetFrameGeometry.width() > dockWidgetFrameGeometry.height()
&& DockAreaWidget->mapToGlobal(dockWidgetFrameGeometry.topLeft()).y() > splitterCenter.y()
&& CDockManager::testConfigFlag(CDockManager::DockContainerHasBottomSideBar))
{
return CDockWidgetSideTab::Bottom;
}
// Then handle left and right
const auto dockWidgetCenter = DockAreaWidget->mapToGlobal(dockWidgetFrameGeometry.center());
const auto calculatedPosition = dockWidgetCenter.x() <= splitterCenter.x() ? CDockWidgetSideTab::Left : CDockWidgetSideTab::Right;
if (calculatedPosition == CDockWidgetSideTab::Right)
{
@ -1534,18 +1551,30 @@ CDockWidgetSideTab::SideTabBarArea CDockContainerWidget::getDockAreaPosition(CDo
return CDockWidgetSideTab::Right;
}
if (CDockManager::testConfigFlag(CDockManager::DockContainerHasLeftSideBar))
{
return CDockWidgetSideTab::Left;
}
return CDockWidgetSideTab::Bottom;
}
if (calculatedPosition == CDockWidgetSideTab::Left)
{
if (CDockManager::testConfigFlag(CDockManager::DockContainerHasLeftSideBar))
{
return CDockWidgetSideTab::Left;
}
if (CDockManager::testConfigFlag(CDockManager::DockContainerHasRightSideBar))
{
return CDockWidgetSideTab::Right;
}
return CDockWidgetSideTab::Bottom;
}
Q_ASSERT_X(false, "CDockContainerWidget::getDockAreaPosition", "Unhandled branch. All positions should be accounted for.");
return CDockWidgetSideTab::Left;
}
@ -2026,15 +2055,21 @@ void CDockContainerWidget::createSideTabBarWidgets()
{
if (CDockManager::testConfigFlag(CDockManager::DockContainerHasLeftSideBar))
{
d->SideTabBarWidgets[CDockWidgetSideTab::Left] = new CSideTabBar(this);
d->SideTabBarWidgets[CDockWidgetSideTab::Left] = new CSideTabBar(this, Qt::Vertical);
d->Layout->addWidget(d->SideTabBarWidgets[CDockWidgetSideTab::Left], 0, 0);
}
if (CDockManager::testConfigFlag(CDockManager::DockContainerHasRightSideBar))
{
d->SideTabBarWidgets[CDockWidgetSideTab::Right] = new CSideTabBar(this);
d->SideTabBarWidgets[CDockWidgetSideTab::Right] = new CSideTabBar(this, Qt::Vertical);
d->Layout->addWidget(d->SideTabBarWidgets[CDockWidgetSideTab::Right], 0, 2);
}
if (CDockManager::testConfigFlag(CDockManager::DockContainerHasBottomSideBar))
{
d->SideTabBarWidgets[CDockWidgetSideTab::Bottom] = new CSideTabBar(this, Qt::Horizontal);
d->Layout->addWidget(d->SideTabBarWidgets[CDockWidgetSideTab::Bottom], 1, 1);
}
}

View File

@ -228,13 +228,16 @@ public:
{
DockContainerHasLeftSideBar = 0x01, //!< If the flag is set left side bar will prioritize showing icons only over text
DockContainerHasRightSideBar = 0x02, //!< If the flag is set right side bar will prioritize showing icons only over text
DockAreaHasAutoHideButton = 0x04, //!< If the flag is set each dock area has a auto hide menu button
LeftSideBarPrioritizeIconOnly = 0x08, //!< If the flag is set each container will have a left side bar
RightSideBarPrioritizeIconOnly = 0x10, //!< If the flag is set each container will have a right side bar
DockAreaOverlayHasTitle = 0x20, //!< If the flag is set overlay dock area title bar will show the window title
DockContainerHasBottomSideBar = 0x04, //!< If the flag is set right side bar will prioritize showing icons only over text
DockAreaHasAutoHideButton = 0x08, //!< If the flag is set each dock area has a auto hide menu button
LeftSideBarPrioritizeIconOnly = 0x10, //!< If the flag is set each container will have a left side bar
RightSideBarPrioritizeIconOnly = 0x20, //!< If the flag is set each container will have a right side bar
BottomSideBarPrioritizeIconOnly = 0x40, //!< If the flag is set right side bar will prioritize showing icons only over text
DockAreaOverlayHasTitle = 0x80, //!< If the flag is set overlay dock area title bar will show the window title
DefaultAutoHideConfig = DockContainerHasLeftSideBar
| DockContainerHasRightSideBar
| DockContainerHasBottomSideBar
| DockAreaHasAutoHideButton
| DockAreaOverlayHasTitle, ///< the default configuration for left and right side bars
};

View File

@ -56,6 +56,7 @@ struct DockWidgetSideTabPrivate
QBoxLayout* TitleLayout; // To have independent spacing from the icon
CSideTabBar* SideTabBar;
QSize IconSize;
Qt::Orientation Orientation{Qt::Vertical};
SideTabIconLabel* IconLabel = nullptr;
QIcon Icon;
@ -89,6 +90,29 @@ struct DockWidgetSideTabPrivate
}
IconLabel->setVisible(true);
}
void updateContentsMargins()
{
QFontMetrics fm(TitleLabel->font());
int Spacing = qRound(fm.height() / 2.0);
if (Orientation == Qt::Vertical)
{
TitleLayout->setContentsMargins(Spacing, Spacing, 0, Spacing);
if (IconLabel)
{
IconLabel->setContentsMargins(Spacing / 2, Spacing / 2, Spacing / 2, 0);
}
}
else if (Orientation == Qt::Horizontal)
{
TitleLayout->setContentsMargins(Spacing,Spacing / 2,Spacing,Spacing);
if (IconLabel)
{
IconLabel->setContentsMargins(Spacing / 2, Spacing / 2, Spacing / 2, 0);
}
}
}
}; // struct DockWidgetTabPrivate
@ -115,7 +139,6 @@ void DockWidgetSideTabPrivate::createLayout()
// Fill the layout
// Purely for spacing on the text without messing up spacing on the icon
TitleLayout = new QBoxLayout(QBoxLayout::TopToBottom);
TitleLayout->setContentsMargins(Spacing,Spacing,0,Spacing);
TitleLayout->addWidget(TitleLabel);
TitleLayout->setSpacing(0);
@ -125,6 +148,8 @@ void DockWidgetSideTabPrivate::createLayout()
_this->setLayout(Layout);
Layout->addLayout(TitleLayout, 1);
updateContentsMargins();
TitleLabel->setVisible(true);
}
@ -241,8 +266,22 @@ void CDockWidgetSideTab::setIconSize(const QSize& Size)
//============================================================================
void CDockWidgetSideTab::updateTitleAndIconVisibility(SideTabBarArea area)
void CDockWidgetSideTab::setOrientation(Qt::Orientation Orientation)
{
d->Orientation = Orientation;
d->Layout->setDirection(Orientation == Qt::Vertical ? QBoxLayout::TopToBottom : QBoxLayout::LeftToRight);
d->TitleLabel->setOrientation(Orientation);
}
//============================================================================
void CDockWidgetSideTab::updateOrientationAndSpacing(SideTabBarArea area)
{
setOrientation(area == Bottom ? Qt::Horizontal : Qt::Vertical);
d->updateContentsMargins();
// Handle Icon changes
if (d->Icon.isNull())
{
return;
@ -251,17 +290,20 @@ void CDockWidgetSideTab::updateTitleAndIconVisibility(SideTabBarArea area)
QFontMetrics fm(d->TitleLabel->font());
int Spacing = qRound(fm.height() / 2.0);
if (CDockManager::testConfigFlag(CDockManager::LeftSideBarPrioritizeIconOnly) && area == Left
|| CDockManager::testConfigFlag(CDockManager::RightSideBarPrioritizeIconOnly) && area == Right)
if (CDockManager::testConfigFlag(CDockManager::LeftSideBarPrioritizeIconOnly) && area == Left)
{
d->TitleLabel->hide();
d->TitleLayout->setContentsMargins(0, 0, 0, 0);
d->IconLabel->setContentsMargins(Spacing / 2, Spacing / 2, Spacing / 2, Spacing / 2);
return;
}
d->TitleLayout->setContentsMargins(Spacing, Spacing, 0, Spacing);
d->IconLabel->setContentsMargins(Spacing / 2, Spacing / 2, Spacing / 2, 0);
if (CDockManager::testConfigFlag(CDockManager::RightSideBarPrioritizeIconOnly) && area == Right)
{
d->TitleLabel->hide();
d->TitleLayout->setContentsMargins(0, 0, 0, 0);
d->IconLabel->setContentsMargins(Spacing / 2, Spacing / 2, Spacing, Spacing / 2);
return;
}
d->TitleLabel->show();
}

View File

@ -78,7 +78,8 @@ public:
enum SideTabBarArea
{
Left,
Right
Right,
Bottom
};
Q_ENUM(SideTabBarArea)
@ -120,15 +121,20 @@ public:
/**
* Set an explicit icon size.
* If no icon size has been set explicitely, than the tab sets the icon size
* If no icon size has been set explicitly, than the tab sets the icon size
* depending on the style
*/
void setIconSize(const QSize& Size);
/**
* Update the title and icon visibility based on the area and the config
* Set orientation vertical or horizontal
*/
void updateTitleAndIconVisibility(SideTabBarArea area);
void setOrientation(Qt::Orientation Orientation);
/**
* Update the orientation, visibility and spacing based on the area and the config
*/
void updateOrientationAndSpacing(SideTabBarArea area);
Q_SIGNALS:
void elidedChanged(bool elided);

View File

@ -241,44 +241,95 @@ QString CElidingLabel::text() const
return d->Text;
}
/**
* Private data of public CVerticalElidingLabel
*/
struct VerticalElidingLabelPrivate
{
CVerticalElidingLabel* _this;
Qt::Orientation Orientation {Qt::Horizontal};
VerticalElidingLabelPrivate(CVerticalElidingLabel* _public);
};
//============================================================================
VerticalElidingLabelPrivate::VerticalElidingLabelPrivate(CVerticalElidingLabel* _public)
{
}
//============================================================================
CVerticalElidingLabel::CVerticalElidingLabel(QWidget* parent, Qt::WindowFlags f) :
CElidingLabel(parent, f)
CElidingLabel(parent, f),
d(new VerticalElidingLabelPrivate(this))
{
}
//============================================================================
void CVerticalElidingLabel::setOrientation(Qt::Orientation orientation)
{
d->Orientation = orientation;
updateGeometry();
}
//============================================================================
void CVerticalElidingLabel::paintEvent(QPaintEvent* event)
{
if (d->Orientation == Qt::Vertical)
{
QPainter painter(this);
painter.rotate(90);
painter.drawText(0,0, QLabel::text());
return;
}
CElidingLabel::paintEvent(event);
}
//============================================================================
QSize CVerticalElidingLabel::sizeHint() const
{
if (d->Orientation == Qt::Vertical)
{
QSize s = CElidingLabel::minimumSizeHint();
return QSize(s.height(), s.width());
}
return CElidingLabel::sizeHint();
}
//============================================================================
QSize CVerticalElidingLabel::minimumSizeHint() const
{
if (d->Orientation == Qt::Vertical)
{
QSize s = CElidingLabel::sizeHint();
return QSize(s.height(), s.width());
}
return CElidingLabel::minimumSizeHint();
}
//============================================================================
int CVerticalElidingLabel::availableWidthForText() const
{
if (d->Orientation == Qt::Vertical)
{
return size().height();
}
return CElidingLabel::availableWidthForText();
}
//============================================================================
int CVerticalElidingLabel::availableWidthForText(QResizeEvent* event) const
{
if (d->Orientation == Qt::Vertical)
{
return event->size().height();
}
return CElidingLabel::availableWidthForText(event);
}
} // namespace QtLabb

View File

@ -36,7 +36,7 @@
namespace ads
{
struct ElidingLabelPrivate;
struct VerticalElidingLabelPrivate;
/**
* A QLabel that supports eliding text.
* Because the functions setText() and text() are no virtual functions setting
@ -108,6 +108,9 @@ Q_SIGNALS:
class CVerticalElidingLabel : public CElidingLabel
{
private:
VerticalElidingLabelPrivate* d;
protected:
void paintEvent(QPaintEvent* event) override;
QSize sizeHint() const override;
@ -117,6 +120,8 @@ protected:
public:
CVerticalElidingLabel(QWidget* parent = 0, Qt::WindowFlags f = Qt::WindowFlags ());
void setOrientation(Qt::Orientation orientation);
}; // class CVerticalElidingLabel
} // namespace QtLabb

View File

@ -58,6 +58,30 @@ struct OverlayDockContainerPrivate
* Private data constructor
*/
OverlayDockContainerPrivate(COverlayDockContainer *_public);
/**
* Convenience function to get a dock widget area
*/
DockWidgetArea getArea(CDockWidgetSideTab::SideTabBarArea area)
{
switch (area)
{
case CDockWidgetSideTab::Left:
{
return LeftDockWidgetArea;
}
case CDockWidgetSideTab::Right:
{
return RightDockWidgetArea;
}
case CDockWidgetSideTab::Bottom:
{
return BottomDockWidgetArea;
}
}
return LeftDockWidgetArea;
}
}; // struct OverlayDockContainerPrivate
//============================================================================
@ -87,22 +111,34 @@ COverlayDockContainer::COverlayDockContainer(CDockManager* DockManager, CDockWid
d->DockArea->updateTitleBarButtonToolTip();
QBoxLayout* l = new QBoxLayout(QBoxLayout::LeftToRight);
d->Splitter = new QSplitter(Qt::Orientation::Horizontal);
d->Splitter = new QSplitter(area == CDockWidgetSideTab::Bottom ? Qt::Orientation::Vertical : Qt::Orientation::Horizontal);
d->Splitter->setObjectName("overlaySplitter");
d->Splitter->setChildrenCollapsible(false);
const auto emptyWidget = new QWidget();
emptyWidget->setMinimumWidth(50); // Prevents you from dragging the splitter too far
emptyWidget->setMinimumWidth(50);
emptyWidget->setMinimumHeight(50); // Prevents you from dragging the splitter too far
if (area == CDockWidgetSideTab::SideTabBarArea::Left)
switch (area)
{
case CDockWidgetSideTab::Left:
{
d->Splitter->addWidget(d->DockArea);
d->Splitter->addWidget(emptyWidget);
break;
}
else
case CDockWidgetSideTab::Right:
{
d->Splitter->addWidget(emptyWidget);
d->Splitter->addWidget(d->DockArea);
break;
}
case CDockWidgetSideTab::Bottom:
{
d->Splitter->addWidget(emptyWidget);
d->Splitter->addWidget(d->DockArea);
break;
}
}
l->setContentsMargins(QMargins());
@ -125,8 +161,16 @@ void COverlayDockContainer::updateMask()
const auto rect = d->DockArea->frameGeometry();
const auto topLeft = rect.topLeft();
const auto handleSize = d->Splitter->handleWidth();
if (d->Area == CDockWidgetSideTab::Bottom)
{
setMask(QRect(QPoint(topLeft.x(), topLeft.y() - handleSize), QSize(rect.size().width(), rect.size().height() + handleSize)));
}
else
{
const auto offset = d->Area == CDockWidgetSideTab::SideTabBarArea::Left ? 0 : handleSize;
setMask(QRect(QPoint(topLeft.x() - offset, topLeft.y()), QSize(rect.size().width() + handleSize, rect.size().height())));
}
}
//============================================================================
@ -194,17 +238,27 @@ void COverlayDockContainer::addDockWidget(CDockWidget* DockWidget)
const auto dockContainerParent = parentContainer();
const auto rootSplitter = dockContainerParent->rootSplitter();
const auto rect = rootSplitter->frameGeometry();
const auto dockWidth = DockWidget->size().width();
if (d->Area == CDockWidgetSideTab::SideTabBarArea::Left)
const auto dockWidth = DockWidget->sizeHint().width();
switch (d->Area)
{
case CDockWidgetSideTab::Left:
{
d->Splitter->setSizes({ dockWidth, rect.width() - dockWidth });
break;
}
else
case CDockWidgetSideTab::Right:
{
d->Splitter->setSizes({ rect.width() - dockWidth, dockWidth });
break;
}
case CDockWidgetSideTab::Bottom:
{
d->Splitter->setSizes({ rect.width() - dockWidth, dockWidth });
break;
}
}
d->DockWidget->sideTabWidget()->updateTitleAndIconVisibility(d->Area);
d->DockWidget->sideTabWidget()->updateOrientationAndSpacing(d->Area);
updateSize();
updateMask();
@ -235,7 +289,7 @@ void COverlayDockContainer::moveContentsToParent()
}
else
{
parentContainer()->addDockWidget(d->Area == CDockWidgetSideTab::Left ? LeftDockWidgetArea : RightDockWidgetArea, d->DockWidget);
parentContainer()->addDockWidget(d->getArea(d->Area), d->DockWidget);
}
cleanupAndDelete();
}
@ -338,13 +392,20 @@ void COverlayDockContainer::collapseView(bool Enable)
//============================================================================
bool COverlayDockContainer::areaExistsInConfig(CDockWidgetSideTab::SideTabBarArea area)
{
if (area == CDockWidgetSideTab::Left && !CDockManager::testConfigFlag(CDockManager::DockContainerHasLeftSideBar))
switch (area)
{
return false;
case CDockWidgetSideTab::Left:
{
return CDockManager::testConfigFlag(CDockManager::DockContainerHasLeftSideBar);
}
if (area == CDockWidgetSideTab::Right && !CDockManager::testConfigFlag(CDockManager::DockContainerHasRightSideBar))
case CDockWidgetSideTab::Right:
{
return false;
return CDockManager::testConfigFlag(CDockManager::DockContainerHasRightSideBar);
}
case CDockWidgetSideTab::Bottom:
{
return CDockManager::testConfigFlag(CDockManager::DockContainerHasBottomSideBar);
}
}
return true;

View File

@ -51,6 +51,7 @@ struct SideTabBarPrivate
CSideTabBar* _this;
CDockContainerWidget* ContainerWidget;
QBoxLayout* TabsLayout;
Qt::Orientation Orientation;
}; // struct SideTabBarPrivate
//============================================================================
@ -61,13 +62,14 @@ SideTabBarPrivate::SideTabBarPrivate(CSideTabBar* _public) :
//============================================================================
CSideTabBar::CSideTabBar(CDockContainerWidget* parent) :
CSideTabBar::CSideTabBar(CDockContainerWidget* parent, Qt::Orientation orientation) :
QWidget(parent),
d(new SideTabBarPrivate(this))
{
d->ContainerWidget = parent;
d->Orientation = orientation;
d->TabsLayout = new QBoxLayout(QBoxLayout::TopToBottom);
d->TabsLayout = new QBoxLayout(d->Orientation == Qt::Vertical ? QBoxLayout::TopToBottom : QBoxLayout::LeftToRight);
d->TabsLayout->setContentsMargins(0, 0, 0, 0);
d->TabsLayout->setSpacing(0);
d->TabsLayout->addStretch(1);
@ -95,7 +97,6 @@ void CSideTabBar::insertSideTab(int Index, CDockWidgetSideTab* SideTab)
//============================================================================
void CSideTabBar::removeSideTab(CDockWidgetSideTab* SideTab)
{
const auto index = d->TabsLayout->indexOf(SideTab);
d->TabsLayout->removeWidget(SideTab);
}
}

View File

@ -56,7 +56,7 @@ public:
/**
* Default Constructor
*/
CSideTabBar(CDockContainerWidget* parent);
CSideTabBar(CDockContainerWidget* parent, Qt::Orientation orientation);
/**
* Virtual Destructor

View File

@ -37,6 +37,11 @@ ads--CDockWidgetSideTab[sideTabBarArea="Right"] {
border-bottom: 1px solid white;
}
ads--CDockWidgetSideTab[sideTabBarArea="Bottom"] {
border-bottom: 3px solid grey;
border-right: 1px solid white;
}
ads--CDockWidgetTab[activeTab="true"] {
background: qlineargradient(spread : pad, x1 : 0, y1 : 0, x2 : 0, y2 : 0.5, stop : 0
palette(window), stop:1 palette(light));
@ -123,6 +128,11 @@ ads--CDockWidgetSideTab:hover[sideTabBarArea="Left"] {
border-left: 3px solid palette(highlight);
}
ads--CDockWidgetSideTab:hover[sideTabBarArea="Bottom"] {
border-bottom: 3px solid palette(highlight);
}
ads--CDockWidgetSideTab[sideTabBarArea="Right"][focused="true"] {
border-right: 3px solid palette(highlight);
}
@ -131,6 +141,10 @@ ads--CDockWidgetSideTab[sideTabBarArea="Left"][focused="true"] {
border-left: 3px solid palette(highlight);
}
ads--CDockWidgetSideTab[sideTabBarArea="Bottom"][focused="true"] {
border-bottom: 3px solid palette(highlight);
}
ads--CDockWidgetTab[focused="true"] {
background: palette(highlight);
border-color: palette(highlight);