title: How to make an expandable/collapsable section widget in Qt
body: <p>I would like to create a custom widget in Qt with the following features:</p>
  
  <ul>
  <li>It is a container</li>
  <li>It may be populated with any Qt layout</li>
  <li>It may be inside any Qt layout</li>
  <li>A button allows to collapse/fold vertically the content, so only the button is visible, all the contained layout is invisible.</li>
  <li>The previous button allows to expand/unfold it again to the size of the layout content.</li>
  <li>The expanding/collapsing is based on sizes (not on show/hide) to allows animation.</li>
  <li>Usable in QDesigner</li>
  </ul>
  
  <p>To provide an idea, here is an image of a similar widget (not Qt):
  <a href="https://i.stack.imgur.com/KsVwP.png" rel="noreferrer"><img src="https://i.stack.imgur.com/KsVwP.png" alt="enter image description here"></a></p>
  
  <p>I already have a frame that work correctly and is exposed in QDesigner. I need now to make it to extend/collapse, which does not seem so simple.</p>
  
  <p>I tried to play with resize(), sizePolicy(), sizeHint() but that does not work:
  When the frame is collapsed I got following values:</p>
  
  <pre><code>sizeHint: (500,20)
  size    : (500,20)
  closestAcceptableSize: (518,150)
  Painted size: (518, 150)
  </code></pre>
  
  <p>QLayout::closestAcceptableSize is not part of the widget so I cannot change it.</p>
  
  <p><strong>Any hint or/and code snippet to achieve that?</strong></p>
  
  <p>EDITED:
  Here a simple example. I removed all except necessary. </p>
  
  <p>main.cpp example</p>
  
  <pre><code>#include &lt;QWidget&gt;
  #include &lt;QPushButton&gt;
  #include &lt;QVBoxLayout&gt;
  
  #include "section.hpp"
  
  
  using namespace myWidgets;
  int main(int argc, char *argv[])
  {
      QApplication a(argc, argv);
  
  
      // Create the main Window
      QWidget window;
      window.resize(500,500);
      window.setStyleSheet("QPushButton:{background-color:rgba(128,128,128,192);}");
  
      // Create the main window layout
      QVBoxLayout topLayout(&amp;window);
      QWidget *w1 = new QWidget();
      w1-&gt;setStyleSheet("background-color:rgba(128,128,128,192);");
      topLayout.addWidget(w1);
  
      Section section(&amp;window);
      topLayout.addWidget(&amp;section);
  
      QVBoxLayout inLayout(&amp;section);
      QPushButton *button = new QPushButton();
      button-&gt;setMinimumHeight(100);
      inLayout.addWidget(button);
  
      QWidget *w2 = new QWidget();
      w2-&gt;setStyleSheet("background-color:rgba(128,128,128,192);");
      topLayout.addWidget(w2);
  
  
  
      window.show();
  
      return a.exec();
  }
  </code></pre>
  
  <p>Section.hpp</p>
  
  <pre><code>#ifndef SECTION_HPP
  #define SECTION_HPP
  
  #include &lt;QPushButton&gt; //for the expand/collapse button
  #include &lt;QtDesigner/QDesignerExportWidget&gt;
  #include &lt;QLayout&gt;
  #include &lt;QPainter&gt;
  #include &lt;QPaintEvent&gt;
  #include &lt;QDebug&gt;
  
  
  // Compatibility for noexcept, not supported in vsc++
  #ifdef _MSC_VER
  #define noexcept throw()
  #endif
  
  #if defined SECTION_BUILD
      #define SECTION_BUILD_DLL_SPEC Q_DECL_EXPORT
  #elif defined SECTION_EXEC
      #define SECTION_BUILD_DLL_SPEC
  #else
      #define SECTION_BUILD_DLL_SPEC Q_DECL_IMPORT
  #endif
  
  namespace myWidgets
  {
  
  class SECTION_BUILD_DLL_SPEC Section : public QWidget
  {
      Q_OBJECT
  
      Q_PROPERTY( bool is_expanded MEMBER isExpanded)
  
  public:
      // Constructor, standard
      explicit Section( QWidget *parent=0 ): QWidget(parent),
          expandButton(this)
      {
          expandButton.resize(20,20);
          expandButton.move(0,0);
          expandButton.connect(&amp;expandButton, &amp;QPushButton::clicked,
                               this, &amp;Section::expandCollapseEvent);
  
          QMargins m= contentsMargins();
          m.setTop(m.top()+25);
          setContentsMargins(m);
          //setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Minimum);
  
      }
  
      virtual void expand( bool expanding ) noexcept
      {
          resize(sizeHint());
          isExpanded = expanding;
          updateGeometry();
  
  qDebug() &lt;&lt; (isExpanded? "expanded":"collapsed") &lt;&lt; sizeHint() &lt;&lt; QWidget::size() &lt;&lt;
              parentWidget()-&gt;layout()-&gt;closestAcceptableSize(this, size());
      }
  
      virtual QSize sizeHint() const noexcept override
      {
          if (isExpanded) return QSize(layout()-&gt;contentsRect().width(),
                                       layout()-&gt;contentsRect().height());
          else return QSize(layout()-&gt;contentsRect().width(), 20);
      }
  
      // Implement custom appearance
      virtual void paintEvent(QPaintEvent *e) noexcept override
      {
          (void) e; //TODO: remove
          QPainter p(this);
          p.setClipRect(e-&gt;rect());
          p.setRenderHint(QPainter::Antialiasing );
          p.fillRect(e-&gt;rect(), QColor(0,0,255,128));
      }
  
  protected:
  
      // on click of the expandButton, collapse/expand this widget
      virtual void expandCollapseEvent() noexcept
      {
          expand(!isExpanded);
      }
  
  
      bool isExpanded = true; //whenever the section is collapsed(false) or expanded(true)
      QPushButton expandButton; //the expanding/collapsing button
  };
  
  }
  
  
  #endif // SECTION_HPP
  </code></pre>