diff --git a/src/AutoHideDockContainer.cpp b/src/AutoHideDockContainer.cpp index e4c3c22..05135bf 100644 --- a/src/AutoHideDockContainer.cpp +++ b/src/AutoHideDockContainer.cpp @@ -34,6 +34,7 @@ #include "SideTabBar.h" #include "DockAreaWidget.h" #include "DockingStateReader.h" +#include "ResizeHandle.h" #include #include @@ -48,6 +49,51 @@ namespace ads { static const int ResizeMargin = 4; +//============================================================================ +bool static isHorizontalArea(CDockWidgetSideTab::SideTabBarArea Area) +{ + switch (Area) + { + case CDockWidgetSideTab::Top: + case CDockWidgetSideTab::Bottom: return true; + case CDockWidgetSideTab::Left: + case CDockWidgetSideTab::Right: return false; + } + + return true; +} + + +//============================================================================ +Qt::Edge static edgeFromSideTabBarArea(CDockWidgetSideTab::SideTabBarArea Area) +{ + switch (Area) + { + case CDockWidgetSideTab::Top: return Qt::BottomEdge; + case CDockWidgetSideTab::Bottom: return Qt::TopEdge; + case CDockWidgetSideTab::Left: return Qt::RightEdge; + case CDockWidgetSideTab::Right: return Qt::LeftEdge; + } + + return Qt::LeftEdge; +} + + +//============================================================================ +int resizeHandleLayoutPosition(CDockWidgetSideTab::SideTabBarArea Area) +{ + switch (Area) + { + case CDockWidgetSideTab::Bottom: + case CDockWidgetSideTab::Right: return 0; + + case CDockWidgetSideTab::Top: + case CDockWidgetSideTab::Left: return 1; + } + + return 0; +} + struct AutoHideDockContainerPrivate { CAutoHideDockContainer* _this; @@ -55,6 +101,9 @@ struct AutoHideDockContainerPrivate CDockWidget* DockWidget{nullptr}; QPointer DockManager{nullptr}; CDockWidgetSideTab::SideTabBarArea SideTabBarArea; + QBoxLayout* Layout; + CResizeHandle* ResizeHandle = nullptr; + QSize Size; /** * Private data constructor @@ -117,35 +166,15 @@ struct AutoHideDockContainerPrivate return QPoint(); } - /* - * Convenience function to get dock area with splitter total size - */ - QRect getDockAreaWithSplitterRect() const + void updateResizeHandleSizeLimitMax() { - const auto rect = DockArea->frameGeometry(); - const auto topLeft = rect.topLeft(); - const auto handleSize = 0; - - if (SideTabBarArea == CDockWidgetSideTab::Bottom) - { - return QRect(QPoint(topLeft.x(), topLeft.y() - handleSize), QSize(rect.size().width(), rect.size().height() + handleSize)); - } - - if (SideTabBarArea == CDockWidgetSideTab::Top) - { - return QRect(QPoint(topLeft.x(), topLeft.y()), QSize(rect.size().width(), rect.size().height() + handleSize)); - } - - auto offset = 0; - if (SideTabBarArea == CDockWidgetSideTab::SideTabBarArea::Right) - { - offset = handleSize; - } - - return QRect(QPoint(topLeft.x() - offset, topLeft.y()), QSize(rect.size().width() + handleSize, rect.size().height())); + auto Rect = DockManager->contentRect(); + ResizeHandle->setMaxResizeSize(ResizeHandle->orientation() == Qt::Horizontal + ? Rect.width() : Rect.height()); } }; // struct AutoHideDockContainerPrivate + //============================================================================ AutoHideDockContainerPrivate::AutoHideDockContainerPrivate( CAutoHideDockContainer *_public) : @@ -154,11 +183,14 @@ AutoHideDockContainerPrivate::AutoHideDockContainerPrivate( } + +//============================================================================ CDockContainerWidget* CAutoHideDockContainer::parentContainer() const { return internal::findParent(this); } + //============================================================================ CAutoHideDockContainer::CAutoHideDockContainer(CDockManager* DockManager, CDockWidgetSideTab::SideTabBarArea area, CDockContainerWidget* parent) : Super(parent), @@ -173,11 +205,16 @@ CAutoHideDockContainer::CAutoHideDockContainer(CDockManager* DockManager, CDockW d->DockArea->updateTitleBarButtonToolTip(); setObjectName("autoHideDockContainer"); - QBoxLayout *l = new QBoxLayout(QBoxLayout::TopToBottom); - l->setContentsMargins(0, 0, 0, 0); - l->setSpacing(0); - setLayout(l); - l->addWidget(d->DockArea); + + d->Layout = new QBoxLayout(isHorizontalArea(area) ? QBoxLayout::TopToBottom : QBoxLayout::LeftToRight); + d->Layout->setContentsMargins(0, 0, 0, 0); + d->Layout->setSpacing(0); + setLayout(d->Layout); + d->Layout->addWidget(d->DockArea); + d->ResizeHandle = new CResizeHandle(edgeFromSideTabBarArea(area), this); + d->ResizeHandle->setMinResizeSize(64); + d->Layout->insertWidget(resizeHandleLayoutPosition(area), d->ResizeHandle); + d->Size = d->DockArea->size(); updateSize(); parent->registerAutoHideWidget(this); @@ -195,33 +232,23 @@ CAutoHideDockContainer::CAutoHideDockContainer(CDockWidget* DockWidget, CDockWid } -//============================================================================ -void CAutoHideDockContainer::updateMask() -{ - //setMask(d->getDockAreaWithSplitterRect()); -} - - //============================================================================ void CAutoHideDockContainer::updateSize() { qDebug() << "CAutoHideDockContainer::updateSize()"; - const auto dockContainerParent = parentContainer(); - const auto rootSplitter = dockContainerParent->rootSplitter(); - const auto rect = rootSplitter->frameGeometry(); + auto dockContainerParent = parentContainer(); + auto rect = dockContainerParent->contentRect(); switch (sideTabBarArea()) { case CDockWidgetSideTab::Top: move(rect.topLeft()); - resize(rect.width(), height()); - setContentsMargins(QMargins(0, 0, 0, ResizeMargin)); + resize(rect.width(), qMin(rect.height(), d->Size.height())); break; case CDockWidgetSideTab::Left: move(rect.topLeft()); - resize(width(), rect.height()); - setContentsMargins(QMargins(0, 0, ResizeMargin, 0)); + resize(qMin(d->Size.width(), rect.width()), rect.height()); break; case CDockWidgetSideTab::Right: @@ -229,8 +256,7 @@ void CAutoHideDockContainer::updateSize() QPoint p = rect.topRight(); p.rx() -= (width() - 1); move(p); - resize(width(), rect.height()); - setContentsMargins(QMargins(ResizeMargin, 0, 0, 0)); + resize(qMin(d->Size.width(), rect.width()), rect.height()); } break; @@ -239,8 +265,7 @@ void CAutoHideDockContainer::updateSize() QPoint p = rect.bottomLeft(); p.ry() -= (height() - 1); move(p); - resize(rect.width(), height()); - setContentsMargins(QMargins(0, ResizeMargin, 0, 0)); + resize(rect.width(), qMin(rect.height(), d->Size.height())); } break; } @@ -297,7 +322,6 @@ void CAutoHideDockContainer::addDockWidget(CDockWidget* DockWidget) this->resize(OldDockArea ? OldDockArea->size() : d->DockWidget->size()); updateSize(); - updateMask(); } @@ -450,12 +474,12 @@ void CAutoHideDockContainer::collapseView(bool Enable) } else { + d->updateResizeHandleSizeLimitMax(); raise(); show(); d->DockArea->show(); d->DockWidget->show(); updateSize(); - updateMask(); d->DockManager->setDockWidgetFocused(d->DockWidget); qApp->installEventFilter(this); } @@ -476,8 +500,10 @@ bool CAutoHideDockContainer::eventFilter(QObject* watched, QEvent* event) { if (event->type() == QEvent::Resize) { - updateSize(); - updateMask(); + if (!d->ResizeHandle->isResizing()) + { + updateSize(); + } } else if (event->type() == QEvent::MouseButtonPress) { @@ -507,8 +533,7 @@ bool CAutoHideDockContainer::eventFilter(QObject* watched, QEvent* event) // user works in it QMouseEvent* me = static_cast(event); auto pos = mapFromGlobal(me->globalPos()); - auto rect = d->getDockAreaWithSplitterRect(); - if (rect.contains(pos)) + if (rect().contains(pos)) { return Super::eventFilter(watched, event); } @@ -538,39 +563,14 @@ bool CAutoHideDockContainer::eventFilter(QObject* watched, QEvent* event) void CAutoHideDockContainer::resizeEvent(QResizeEvent* event) { std::cout << "ResizeEvent" << std::endl; - updateMask(); Super::resizeEvent(event); -} - - -//============================================================================ -bool CAutoHideDockContainer::event(QEvent *event) -{ - return Super::event(event); -} - - -//============================================================================ -void CAutoHideDockContainer::mouseMoveEvent(QMouseEvent *event) -{ - qDebug() << "mouseMoveEvent: " << event->pos(); - qDebug() << "height: " << height(); - auto p = event->pos(); - if (p.y() > (height() - ResizeMargin - 1) && p.y() < height()) + if (d->ResizeHandle->isResizing()) { - setCursor(Qt::SizeVerCursor); - } - else - { - setCursor(Qt::ArrowCursor); + d->Size = this->size(); + qDebug() << "Size " << d->Size; + d->updateResizeHandleSizeLimitMax(); } } - -//============================================================================ -void CAutoHideDockContainer::mousePressEvent(QMouseEvent *event) -{ - qDebug() << "mousePressEvent: " << event->pos(); -} } diff --git a/src/AutoHideDockContainer.h b/src/AutoHideDockContainer.h index 1bfc36a..821f490 100644 --- a/src/AutoHideDockContainer.h +++ b/src/AutoHideDockContainer.h @@ -61,11 +61,7 @@ private: protected: bool eventFilter(QObject* watched, QEvent* event) override; void resizeEvent(QResizeEvent* event) override; - void updateMask(); void updateSize(); - virtual bool event(QEvent *event) override; - virtual void mouseMoveEvent(QMouseEvent *event) override; - virtual void mousePressEvent(QMouseEvent *event) override; CDockContainerWidget* parentContainer() const; diff --git a/src/DockContainerWidget.cpp b/src/DockContainerWidget.cpp index 5aba7e1..e7fa561 100644 --- a/src/DockContainerWidget.cpp +++ b/src/DockContainerWidget.cpp @@ -2276,7 +2276,7 @@ CSideTabBar* CDockContainerWidget::sideTabBar(CDockWidgetSideTab::SideTabBarArea //============================================================================ -QRect CDockContainerWidget::contentRect() +QRect CDockContainerWidget::contentRect() const { if (!d->RootSplitter) { @@ -2285,6 +2285,17 @@ QRect CDockContainerWidget::contentRect() return d->RootSplitter->geometry(); } + + +//=========================================================================== +QRect CDockContainerWidget::contentRectGlobal() const +{ + if (!d->RootSplitter) + { + return QRect(); + } + return internal::globalGeometry(d->RootSplitter); +} } // namespace ads //--------------------------------------------------------------------------- diff --git a/src/DockContainerWidget.h b/src/DockContainerWidget.h index 005d37a..e33a3a8 100644 --- a/src/DockContainerWidget.h +++ b/src/DockContainerWidget.h @@ -331,7 +331,16 @@ public: /** * Returns the content rectangle without the autohide side bars */ - QRect contentRect(); + QRect contentRect() const; + + /** + * Returns the content rectangle mapped to global space. + * The content rectangle is the rectangle of the dock manager widget minus + * the auto hide side bars. So that means, it is the geometry of the + * internal root splitter mapped to global space. + */ + QRect contentRectGlobal() const; + Q_SIGNALS: /** diff --git a/src/DockManager.cpp b/src/DockManager.cpp index 845d23b..d349b1a 100644 --- a/src/DockManager.cpp +++ b/src/DockManager.cpp @@ -1312,6 +1312,7 @@ void CDockManager::setFloatingContainersTitle(const QString& Title) FloatingContainersTitle = Title; } + //=========================================================================== QString CDockManager::floatingContainersTitle() { diff --git a/src/ResizeHandle.cpp b/src/ResizeHandle.cpp new file mode 100644 index 0000000..fd4874f --- /dev/null +++ b/src/ResizeHandle.cpp @@ -0,0 +1,269 @@ +//============================================================================ +/// \file ResizeHandle.cpp +/// \author Uwe Kindler +/// \date 24.10.2022 +/// \brief Implementation of CResizeHandle class +//============================================================================ + +//============================================================================ +// INCLUDES +//============================================================================ +#include "ResizeHandle.h" + +#include +#include +#include +#include + +#include "ads_globals.h" + +namespace ads +{ +/** + * Private data class of CResizeHandle class (pimpl) + */ +struct ResizeHandlePrivate +{ + CResizeHandle *_this; + Qt::Edge HandlePosition = Qt::LeftEdge; + QWidget* Target = nullptr; + int MouseOffset = 0; + bool Pressed = false; + int MinSize = 0; + int MaxSize = 1; + + /** + * Private data constructor + */ + ResizeHandlePrivate(CResizeHandle *_public); + + /** + * Pick position component from pos depending on orientation + */ + int pick(const QPoint &pos) const + { + return _this->orientation() == Qt::Horizontal ? pos.x() : pos.y(); + } + + /** + * Returns true, if orientation is horizontal + */ + bool isHorizontal() const + { + return _this->orientation() == Qt::Horizontal; + } +}; +// struct ResizeHandlePrivate + +//============================================================================ +ResizeHandlePrivate::ResizeHandlePrivate(CResizeHandle *_public) : + _this(_public) +{ + +} + +//============================================================================ +CResizeHandle::CResizeHandle(Qt::Edge HandlePosition, QWidget* parent) : + Super(parent), + d(new ResizeHandlePrivate(this)) +{ + d->Target = parent; + setHandlePosition(HandlePosition); + setMinResizeSize(48); + setMaxResizeSize(d->isHorizontal() ? parent->height() : parent->width()); + + if (!d->isHorizontal()) + { + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + } + else + { + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); + } +} + + +//============================================================================ +CResizeHandle::~CResizeHandle() +{ + delete d; +} + + +//============================================================================ +void CResizeHandle::mouseMoveEvent(QMouseEvent* e) +{ + if (!(e->buttons() & Qt::LeftButton)) + { + return; + } + int pos = d->pick(e->pos()) - d->MouseOffset; + auto OldGeometry = d->Target->geometry(); + auto NewGeometry = OldGeometry; + switch (d->HandlePosition) + { + case Qt::LeftEdge: + { + NewGeometry.adjust(pos, 0, 0, 0); + int Size = qBound(d->MinSize, NewGeometry.width(), d->MaxSize); + NewGeometry.setWidth(Size); + NewGeometry.moveTopRight(OldGeometry.topRight()); + } + break; + + + case Qt::RightEdge: + { + NewGeometry.adjust(0, 0, pos, 0); + int Size = qBound(d->MinSize, NewGeometry.width(), d->MaxSize); + NewGeometry.setWidth(Size); + } + break; + + case Qt::TopEdge: + { + NewGeometry.adjust(0, pos, 0, 0); + int Size = qBound(d->MinSize, NewGeometry.height(), d->MaxSize); + NewGeometry.setHeight(Size); + NewGeometry.moveBottomLeft(OldGeometry.bottomLeft()); + } + break; + + case Qt::BottomEdge: + { + NewGeometry.adjust(0, 0, 0, pos); + int Size = qBound(d->MinSize, NewGeometry.height(), d->MaxSize); + NewGeometry.setHeight(Size); + } + break; + } + d->Target->setGeometry(NewGeometry); + //qDebug() << "globalGeometry(): " << internal::globalGeometry(d->Target); + //qDebug() << "parentGlobalGeometry(): " << internal::globalGeometry(d->Target->parentWidget()); + + + /*if (opaqueResize()) + { + moveSplitter(pos); + } + else + { + //d->s->setRubberBand(closestLegalPosition(pos)); + }*/ +} + + +//============================================================================ +void CResizeHandle::mousePressEvent(QMouseEvent* e) +{ + qDebug() << "CResizeHandle::mousePressEvent " << e; + if (e->button() == Qt::LeftButton) + { + d->MouseOffset = d->pick(e->pos()); + d->Pressed = true; + update(); + } +} + + +//============================================================================ +void CResizeHandle::mouseReleaseEvent(QMouseEvent* e) +{ + qDebug() << "CResizeHandle::mouseReleaseEvent " << e; + /*if (!opaqueResize() && e->button() == Qt::LeftButton) { + int pos = d->pick(parentWidget()->mapFromGlobal(e->globalPos())) + - d->mouseOffset; + d->s->setRubberBand(-1); + moveSplitter(pos); + }*/ + if (e->button() == Qt::LeftButton) + { + d->Pressed = false; + update(); + } +} + + +//============================================================================ +void CResizeHandle::setHandlePosition(Qt::Edge HandlePosition) +{ + d->HandlePosition = HandlePosition; + switch (d->HandlePosition) + { + case Qt::LeftEdge: // fall through + case Qt::RightEdge: setCursor(Qt::SizeHorCursor); break; + + case Qt::TopEdge: // fall through + case Qt::BottomEdge: setCursor(Qt::SizeVerCursor); break; + } +} + + +//============================================================================ +Qt::Edge CResizeHandle::handlePostion() const +{ + return d->HandlePosition; +} + + +//============================================================================ +Qt::Orientation CResizeHandle::orientation() const +{ + switch (d->HandlePosition) + { + case Qt::LeftEdge: // fall through + case Qt::RightEdge: return Qt::Horizontal; + + case Qt::TopEdge: // fall through + case Qt::BottomEdge: return Qt::Vertical; + } + + return Qt::Horizontal; +} + + +//============================================================================ +QSize CResizeHandle::sizeHint() const +{ + QSize Result; + switch (d->HandlePosition) + { + case Qt::LeftEdge: // fall through + case Qt::RightEdge: Result = QSize(4, d->Target->height()); break; + + case Qt::TopEdge: // fall through + case Qt::BottomEdge: Result = QSize(d->Target->width(), 4); break; + } + + qDebug() << "CResizeHandle::sizeHint(): " << Result; + return Result; + /*parentWidget()->style()->sizeFromContents(QStyle::CT_Splitter, &opt, QSize(hw, hw), d->s) + .expandedTo(QApplication::globalStrut());*/ +} + + +//============================================================================ +bool CResizeHandle::isResizing() const +{ + return d->Pressed; +} + + +//============================================================================ +void CResizeHandle::setMinResizeSize(int MinSize) +{ + qDebug() << "CResizeHandle::setMinResizeSize " << MinSize; + d->MinSize = MinSize; +} + + +//============================================================================ +void CResizeHandle::setMaxResizeSize(int MaxSize) +{ + qDebug() << "CResizeHandle::setMaxResizeSize " << MaxSize; + d->MaxSize = MaxSize; +} +} // namespace ads + +//--------------------------------------------------------------------------- +// EOF ResizeHandle.cpp diff --git a/src/ResizeHandle.h b/src/ResizeHandle.h new file mode 100644 index 0000000..a7a1360 --- /dev/null +++ b/src/ResizeHandle.h @@ -0,0 +1,90 @@ +#ifndef ResizeHandleH +#define ResizeHandleH +//============================================================================ +/// \file ResizeHandle.h +/// \author Uwe Kindler +/// \date 24.10.2022 +/// \brief Declaration of CResizeHandle class +//============================================================================ + +//============================================================================ +// INCLUDES +//============================================================================ +#include + +namespace ads +{ +struct ResizeHandlePrivate; + +/** + * Resize handle for resizing its parent widget + */ +class CResizeHandle : public QFrame +{ + Q_OBJECT + Q_DISABLE_COPY(CResizeHandle) +private: + ResizeHandlePrivate* d; ///< private data (pimpl) + friend struct ResizeHandlePrivate; + +protected: + void mouseMoveEvent(QMouseEvent *) override; + void mousePressEvent(QMouseEvent *) override; + void mouseReleaseEvent(QMouseEvent *) override; + +public: + using Super = QFrame; + + /** + * Default Constructor + */ + CResizeHandle(Qt::Edge HandlePosition, QWidget* parent); + + /** + * Virtual Destructor + */ + virtual ~CResizeHandle(); + + /** + * Sets the handle position + */ + void setHandlePosition(Qt::Edge HandlePosition); + + /** + * Returns the handle position + */ + Qt::Edge handlePostion() const; + + /** + * Returns the orientation of this resize handle + */ + Qt::Orientation orientation() const; + + /** + * Returns the size hint + */ + QSize sizeHint() const override; + + /** + * Returns true, if resizing is active + */ + bool isResizing() const; + + /** + * Sets the minimum size for the widget that is going to be resized. + * The resize handle will not resize the target widget to a size smaller + * than this value + */ + void setMinResizeSize(int MinSize); + + /** + * Sets the maximum size for the widget that is going to be resized + * The resize handle will not resize the target widget to a size bigger + * than this value + */ + void setMaxResizeSize(int MaxSize); +}; // class name +} + // namespace ads +//----------------------------------------------------------------------------- +#endif // ResizeHandleH diff --git a/src/ads_globals.cpp b/src/ads_globals.cpp index 8eca159..3971abf 100644 --- a/src/ads_globals.cpp +++ b/src/ads_globals.cpp @@ -406,6 +406,15 @@ void repolishStyle(QWidget* w, eRepolishChildOptions Options) } } + +//============================================================================ +QRect globalGeometry(QWidget* w) +{ + QRect g = w->geometry(); + g.moveTopLeft(w->mapToGlobal(QPoint(0, 0))); + return g; +} + } // namespace internal } // namespace ads diff --git a/src/ads_globals.h b/src/ads_globals.h index 4748c27..d8021b8 100644 --- a/src/ads_globals.h +++ b/src/ads_globals.h @@ -307,6 +307,11 @@ enum eRepolishChildOptions void repolishStyle(QWidget* w, eRepolishChildOptions Options = RepolishIgnoreChildren); +/** + * Returns the geometry of the given widget in global space + */ +QRect globalGeometry(QWidget* w); + } // namespace internal } // namespace ads diff --git a/src/src.pro b/src/src.pro index d0422db..101f7a3 100644 --- a/src/src.pro +++ b/src/src.pro @@ -51,7 +51,8 @@ HEADERS += \ AutoHideDockContainer.h \ SideTabBar.h \ DockWidgetSideTab.h \ - PushButton.h + PushButton.h \ + ResizeHandle.h SOURCES += \ @@ -75,7 +76,8 @@ SOURCES += \ AutoHideDockContainer.cpp \ SideTabBar.cpp \ DockWidgetSideTab.cpp \ - PushButton.cpp + PushButton.cpp \ + ResizeHandle.cpp unix:!macx { diff --git a/src/stylesheets/default.css b/src/stylesheets/default.css index 7946d62..9dbf789 100644 --- a/src/stylesheets/default.css +++ b/src/stylesheets/default.css @@ -197,3 +197,9 @@ ads--CAutoHideDockContainer #dockAreaAutoHideButton #autoHideTitleLabel { padding-left: 4px; } + +ads--CResizeHandle +{ + background: red; + border: 1px solid green; +} \ No newline at end of file