3

I came across this post answer on How to make an expandable/collapsable section widget in Qt, but the proposed solution is not without flaws.

In fact, as you can see below, once the widget has been expanded and closed, it doesn't reset the window size, which has the effect of creating empty spaces:

enter image description here

Here is a minimal reproducible example (you need to import the spoiler widget from the post answer):

QApplication a(argc, argv);
QWidget* widget = new QWidget();
widget->setFixedWidth(500);

QVBoxLayout* mainLayout = new QVBoxLayout(widget);

QFrame* widgetTop = new QFrame();
widgetTop->setFrameShape(QFrame::StyledPanel);
widgetTop->setFixedHeight(50);
QHBoxLayout* layoutTop = new QHBoxLayout(widgetTop);
QLabel* labelTop = new QLabel("widget-top");
labelTop->setAlignment(Qt::AlignCenter);
layoutTop->addWidget(labelTop);
mainLayout->addWidget(widgetTop);


QFrame* widgetMiddle = new QFrame();
widgetMiddle->setFrameShape(QFrame::StyledPanel);
widgetMiddle->setFixedHeight(150);
QHBoxLayout* layoutMiddle = new QHBoxLayout(widgetMiddle);
QLabel* labelMiddle = new QLabel("widget-middle");
labelMiddle->setAlignment(Qt::AlignCenter);
layoutMiddle->addWidget(labelMiddle);
mainLayout->addWidget(widgetMiddle);


QFrame* widgetBottom = new QFrame();
widgetBottom->setFrameShape(QFrame::StyledPanel);
widgetBottom->setFixedHeight(100);
QHBoxLayout* layoutBottom = new QHBoxLayout(widgetBottom);
QLabel* labelBottom = new QLabel("widget-bottom");
labelBottom->setAlignment(Qt::AlignCenter);
layoutBottom->addWidget(labelBottom);

auto* anyLayout = new QVBoxLayout();
anyLayout->addWidget(widgetBottom);
Spoiler* spoiler = new Spoiler("I'm buggy", 100);
spoiler->setContentLayout(*anyLayout);
mainLayout->addWidget(spoiler);

// Affichage du widget principal
widget->show();
return a.exec();

I tried using adjustSize() but it doesn't work with the QPropertyAnimation.

How to make the window return to its original height?

Hippo
  • 31
  • 6

1 Answers1

1

Here are a few changes I made to Spoiler to deal with this problem, and you have to make your window spoiler's parent widget:

Added more members:

bool collapsing=false;
int contentHeight;

Changes made in ctor:

QObject::connect(&toggleButton, &QToolButton::clicked, [this](const bool checked)
{
    toggleButton.setArrowType(checked ? Qt::ArrowType::DownArrow : Qt::ArrowType::RightArrow);
    toggleAnimation.setDirection(checked ? QAbstractAnimation::Forward : QAbstractAnimation::Backward);

    //a check for the resize event 
    if(toggleAnimation.direction()==QAbstractAnimation::Backward)
    {
        collapsing=true;
    }
    else
    {
        collapsing=false;
    }
    
    toggleAnimation.start();
});

//get spoiler min and max sizes to normal when animation finishes
QObject::connect(&toggleAnimation, &QParallelAnimationGroup::finished, [this]()
{
    parentWidget()->setMaximumHeight(16777215);
    parentWidget()->setMinimumHeight(parentWidget()->minimumSizeHint().height());
});

Resize event:

void Spoiler::resizeEvent(QResizeEvent *event)
{
    //resize parentWidget while spoiler size is animated
    //second condition is the explanation behind the problematic behavior
    if(toggleAnimation.state()==QAbstractAnimation::Running &&
        parentWidget()->minimumHeight()+contentHeight>parentWidget()->height())
    {
        if(collapsing)
        {
            int newHeight = parentWidget()->height()-(event->oldSize().height()-event->size().height());
            parentWidget()->setFixedHeight(newHeight);
            return;
        }

        int newHeight = parentWidget()->height()-(event->oldSize().height()-event->size().height());
        parentWidget()->setFixedHeight(newHeight);
    }
}

Here is how it looks at first sight:

Less buggy at first

But if you stress it by fast clicking it, it will behave wrong:

Bug appears again after stressing it out

The resize event getting called so many times too fast is the reason behind that, but I don't know how exactly.

I did try to animate parent widget size, but it's uglier than this solution, perhaps because I couldn't figure the start and end values.

I'm providing this more as a help to identify the problem and suggest a way to deal with it, I don't think this solution should be used, unless you can make it work for your use case.