From aa7714210a2da7510292585ed4507460976da587 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Thu, 27 May 2021 20:55:31 +0200 Subject: [PATCH] Rework section widget to support dynamically sized content --- src/headers/section.hpp | 18 ++++- src/section.cpp | 149 +++++++++++++++++++++++++--------------- 2 files changed, 110 insertions(+), 57 deletions(-) diff --git a/src/headers/section.hpp b/src/headers/section.hpp index 8e3154a2..9dea41b2 100644 --- a/src/headers/section.hpp +++ b/src/headers/section.hpp @@ -14,18 +14,30 @@ public: explicit Section(const int animationDuration = 300, QWidget *parent = 0); - void SetContent(QWidget *w); + void SetContent(QWidget *w, bool collapsed = true); void AddHeaderWidget(QWidget *); -public slots: - void Collapse(bool collapsed); +protected: + bool eventFilter(QObject *obj, QEvent *event) override; + +private slots: + void AnimationFinished(); + void Collapse(bool collapse); private: + void SetupAnimations(); + void CleanUpPreviousContent(); + QGridLayout *_mainLayout; QHBoxLayout *_headerWidgetLayout; QToolButton *_toggleButton; QFrame *_headerLine; QParallelAnimationGroup *_toggleAnimation = nullptr; + QParallelAnimationGroup *_contentAnimation = nullptr; QScrollArea *_contentArea = nullptr; + QWidget *_content = nullptr; int _animationDuration; + std::atomic_bool _transitioning = {false}; + int _headerHeight = 0; + int _contentHeight = 0; }; diff --git a/src/section.cpp b/src/section.cpp index 9713950c..c76b6800 100644 --- a/src/section.cpp +++ b/src/section.cpp @@ -1,7 +1,9 @@ -#include #include "headers/section.hpp" #include "headers/utility.hpp" +#include +#include + Section::Section(const int animationDuration, QWidget *parent) : QWidget(parent), _animationDuration(animationDuration) { @@ -38,18 +40,102 @@ Section::Section(const int animationDuration, QWidget *parent) connect(_toggleButton, &QToolButton::toggled, this, &Section::Collapse); } -void Section::Collapse(bool collapsed) +void Section::Collapse(bool collapse) { - _toggleButton->setChecked(collapsed); - _toggleButton->setArrowType(!collapsed ? Qt::ArrowType::DownArrow - : Qt::ArrowType::RightArrow); - _toggleAnimation->setDirection(!collapsed + _toggleButton->setChecked(collapse); + _toggleButton->setArrowType(!collapse ? Qt::ArrowType::DownArrow + : Qt::ArrowType::RightArrow); + _toggleAnimation->setDirection(!collapse ? QAbstractAnimation::Forward : QAbstractAnimation::Backward); + _transitioning = true; _toggleAnimation->start(); } -void Section::SetContent(QWidget *w) +void Section::SetContent(QWidget *w, bool collapsed) +{ + CleanUpPreviousContent(); + delete _contentArea; + + // Setup contentArea + _contentArea = new QScrollArea(this); + _contentArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + _contentArea->setStyleSheet("QScrollArea { border: none; }"); + _contentArea->setMaximumHeight(0); + _contentArea->setMinimumHeight(0); + + w->installEventFilter(this); + _content = w; + auto newLayout = new QVBoxLayout(); + newLayout->setContentsMargins(0, 0, 0, 0); + newLayout->addWidget(w); + _contentArea->setLayout(newLayout); + _mainLayout->addWidget(_contentArea, 1, 0, 1, 3); + + _headerHeight = sizeHint().height() - _contentArea->maximumHeight(); + _contentHeight = newLayout->sizeHint().height(); + + if (collapsed) { + this->setMinimumHeight(_headerHeight); + _contentArea->setMaximumHeight(0); + } else { + this->setMinimumHeight(_headerHeight + _contentHeight); + _contentArea->setMaximumHeight(_contentHeight); + } + SetupAnimations(); + Collapse(collapsed); +} + +void Section::AddHeaderWidget(QWidget *w) +{ + _headerWidgetLayout->addWidget(w); +} + +bool Section::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::Resize && !_transitioning) { + _contentHeight = _content->sizeHint().height(); + setMaximumHeight(_headerHeight + _contentHeight); + setMinimumHeight(_headerHeight + _contentHeight); + _contentArea->setMaximumHeight(_contentHeight); + SetupAnimations(); + } + return QObject::eventFilter(obj, event); +} + +void Section::SetupAnimations() +{ + delete _toggleAnimation; + + _toggleAnimation = new QParallelAnimationGroup(this); + _toggleAnimation->addAnimation( + new QPropertyAnimation(this, "minimumHeight")); + _toggleAnimation->addAnimation( + new QPropertyAnimation(this, "maximumHeight")); + _toggleAnimation->addAnimation( + new QPropertyAnimation(_contentArea, "maximumHeight")); + + for (int i = 0; i < _toggleAnimation->animationCount() - 1; ++i) { + QPropertyAnimation *SectionAnimation = + static_cast( + _toggleAnimation->animationAt(i)); + SectionAnimation->setDuration(_animationDuration); + SectionAnimation->setStartValue(_headerHeight); + SectionAnimation->setEndValue(_headerHeight + _contentHeight); + } + + QPropertyAnimation *contentAnimation = + static_cast(_toggleAnimation->animationAt( + _toggleAnimation->animationCount() - 1)); + contentAnimation->setDuration(_animationDuration); + contentAnimation->setStartValue(0); + contentAnimation->setEndValue(_contentHeight); + + QWidget::connect(_toggleAnimation, SIGNAL(finished()), this, + SLOT(AnimationFinished())); +} + +void Section::CleanUpPreviousContent() { // Clean up previous content if (_contentArea) { @@ -59,54 +145,9 @@ void Section::SetContent(QWidget *w) delete oldLayout; } } - - delete _contentArea; - delete _toggleAnimation; - - // Setup contentArea - _contentArea = new QScrollArea(this); - _contentArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - - // Start out collapsed - _contentArea->setMaximumHeight(0); - _contentArea->setMinimumHeight(0); - - auto newLayout = new QVBoxLayout(); - newLayout->addWidget(w); - _contentArea->setLayout(newLayout); - _mainLayout->addWidget(_contentArea, 1, 0, 1, 3); - - // Animation Setup - _toggleAnimation = new QParallelAnimationGroup(this); - _toggleAnimation->addAnimation( - new QPropertyAnimation(this, "minimumHeight")); - _toggleAnimation->addAnimation( - new QPropertyAnimation(this, "maximumHeight")); - _toggleAnimation->addAnimation( - new QPropertyAnimation(_contentArea, "maximumHeight")); - - const auto collapsedHeight = - sizeHint().height() - _contentArea->maximumHeight(); - auto contentHeight = newLayout->sizeHint().height(); - - for (int i = 0; i < _toggleAnimation->animationCount() - 1; ++i) { - QPropertyAnimation *SectionAnimation = - static_cast( - _toggleAnimation->animationAt(i)); - SectionAnimation->setDuration(_animationDuration); - SectionAnimation->setStartValue(collapsedHeight); - SectionAnimation->setEndValue(collapsedHeight + contentHeight); - } - - QPropertyAnimation *contentAnimation = - static_cast(_toggleAnimation->animationAt( - _toggleAnimation->animationCount() - 1)); - contentAnimation->setDuration(_animationDuration); - contentAnimation->setStartValue(0); - contentAnimation->setEndValue(contentHeight); } -void Section::AddHeaderWidget(QWidget *w) +void Section::AnimationFinished() { - _headerWidgetLayout->addWidget(w); + _transitioning = false; }