Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Layout does not change sizeHint to fit children

Layout does not change sizeHint to fit children

Scheduled Pinned Locked Moved Solved General and Desktop
8 Posts 2 Posters 563 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • A Offline
    A Offline
    AntonBogun
    wrote on last edited by
    #1

    Hello,
    This question is somewhat of a continuation from my last question, but now I tried to add more structure to my widget:

    //notificationwidget.h
    class NotificationScrollArea : public QScrollArea {
        Q_OBJECT
    public:
        explicit NotificationScrollArea(QWidget *parent = nullptr) : QScrollArea(parent){};
        QSize sizeHint() const override {
            return widget()->sizeHint()+QSize(0,2);
        }
    };
    
    class NotificationWidget : public QWidget {
        Q_OBJECT
    public:
        explicit NotificationWidget(QWidget *parent = nullptr);
    
        void addNotification(const QString &message);
        void debugHint() const;
        QSize sizeHint() const override;
    
    protected:
        void resizeEvent(QResizeEvent *event) override;
        void _adjustSize();
    private:
    
        QWidget *tabWidget;
        QHBoxLayout *tabLayout;
        QPushButton *collapseButton;
        QPushButton *closeButton;
        QVBoxLayout *layout;
    
        QScrollArea *scrollArea;
        QWidget *scrollWidget;
        QVBoxLayout *scrollLayout;
    };
    
    //notificationwidget.cpp
    #include "notificationwidget.h"
    #include <QLabel>
    
    NotificationWidget::NotificationWidget(QWidget *parent) 
        : QWidget(parent),
        tabWidget(new QWidget(this)),
        tabLayout(new QHBoxLayout(tabWidget)),
        layout(new QVBoxLayout(this)),
        collapseButton(new QPushButton("Collapse", this)),
        closeButton(new QPushButton("Close", this)),
        scrollArea(new NotificationScrollArea(this)),
        scrollWidget(new QWidget),
        scrollLayout(new QVBoxLayout(scrollWidget))
    {
        tabLayout->addWidget(collapseButton);
        tabLayout->addWidget(closeButton);
        tabWidget->setLayout(tabLayout);
    
        layout->addWidget(tabWidget);
        layout->addWidget(scrollArea);
        setLayout(layout);
    
        scrollArea->setWidget(scrollWidget);
        scrollArea->setWidgetResizable(true);
        scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
        scrollWidget->setLayout(scrollLayout);
    
        setFixedWidth(300);
        qDebug() << "INITIAL";
        debugHint();
    }
    
    void NotificationWidget::debugHint() const {
        qDebug() << "sizeHint:" << sizeHint()
        << "layout:" << layout->sizeHint()
        << "scrollWidget:" << scrollWidget->sizeHint()
        << "scrollArea:" << scrollArea->sizeHint()
        << "scrollLayout:" << scrollLayout->sizeHint()
        << "tab:" << tabWidget->sizeHint()
        << "tabL:" << tabLayout->sizeHint()
        << "collapse:" << collapseButton->sizeHint()
        << "close:" << closeButton->sizeHint();
    }
    
    QSize NotificationWidget::sizeHint() const {
        return layout->sizeHint();
    }
    
    void NotificationWidget::addNotification(const QString &message) {
        QLabel *label = new QLabel(message, this);
        label->setWordWrap(true);
        scrollLayout->addWidget(label);
        _adjustSize();
        qDebug() << "AFTER ADD" << message << ":";
        debugHint();
    }
    
    void NotificationWidget::_adjustSize() {
        scrollWidget->adjustSize();
        scrollArea->adjustSize();
        adjustSize();
    }
    
    void NotificationWidget::resizeEvent(QResizeEvent *event) {
        QWidget::resizeEvent(event);
        _adjustSize();
    }
    
    //main.cpp
    #include <QApplication>
    #include <QWidget>
    #include <QVBoxLayout>
    #include <QHBoxLayout>
    #include <QPushButton>
    #include <QScrollArea>
    #include <QResizeEvent>
    #include <QDebug>
    #include <QMainWindow>
    
    #include "NotificationWidget.h"
    
    int main(int argc, char *argv[]) {
        QApplication app(argc, argv);
        QMainWindow mainWindow;
        mainWindow.setGeometry(100, 100, 800, 500);
    
        NotificationWidget *notificationWidget = new NotificationWidget(&mainWindow);
        mainWindow.setCentralWidget(notificationWidget);
    
        for (int i = 0; i < 10; ++i) {
            notificationWidget->addNotification(QString("Notification %1").arg(i + 1));
        }
    
        mainWindow.show();
        return app.exec();
    }
    

    The problem with this is that the sizeHint of the NotificationWidget's layout and therefore (see sizeHint override) of itself does not get updated with the change of the contents, so even when there is plenty of extra space it ends up taking this seemingly arbitrary small size and forcing the scrollArea to use a scroll bar:
    79d49c3f-9265-4394-9b20-8d9d289d036e-image.png

    The intended behavior is that it takes a constant horizontal space and as much vertical space as the parent allows, and fits both the tabWidget and the scrollArea in this space, giving as much vertical space as possible to the tab widget.

    What am I missing in the layout usage here to make it work correctly?

    1 Reply Last reply
    0
    • A Offline
      A Offline
      AntonBogun
      wrote on last edited by
      #8

      Ok, after a lot of pain and reading up on source code of updateGeometry() in QWidget and activate()/update() in QLayout, and also tracking down the issue of a new label being not shown initially (which is really weird because in another project it was fine), I got the exact behaviour I want:

      class NotificationScrollArea : public QScrollArea {
      public:
          explicit NotificationScrollArea(QWidget *parent = nullptr) : QScrollArea(parent){};
          QSize sizeHint() const override {
              return widget()->sizeHint()+QSize(0,2);
          }
      };
      
      class NotificationWidget : public QWidget {
      public:
          int i=0;
          NotificationWidget(QWidget *parent = nullptr) : QWidget(parent) {
              QVBoxLayout* vlayout = new QVBoxLayout(this);
              QPushButton* collapse = new QPushButton("Collapse");
              QPushButton* close = new QPushButton("Close");
              QHBoxLayout* hlayout = new QHBoxLayout;
              vlayout->addLayout(hlayout);
              hlayout->addWidget(collapse);
              hlayout->addWidget(close);
              hlayout->setAlignment(Qt::AlignRight);
      
              QScrollArea* scrollArea	= new NotificationScrollArea;
              setMaximumWidth(300);
              vlayout->addWidget(scrollArea);
      
      
              QWidget *scrollContent = new QWidget(scrollArea);
              QVBoxLayout *scrollLayout = new QVBoxLayout(scrollContent);
      
              for (; i < 10; ++i) {
                  QLabel *label = new QLabel(QString("Label  %1").arg(i+1), scrollContent);
                  label->setWordWrap(1);
                  scrollLayout->addWidget(label);
              }
              
              scrollContent->setLayout(scrollLayout);
              scrollArea->setWidget(scrollContent);
              scrollArea->setWidgetResizable(true);
      
              scrollArea->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
              connect(collapse,&QPushButton::clicked,[this,scrollArea,scrollLayout,scrollContent](){
                  i++;
                  QLabel *label = new QLabel(QString("Label  %1").arg(i), scrollContent);
                  label->setWordWrap(1);
                  scrollLayout->addWidget(label);
                  label->show();
                  scrollContent->updateGeometry();
                  scrollArea->updateGeometry();
                  updateGeometry();
              });
          }
      };
      //MainWindow and main code same as before
      

      I'm pretty sure every single line of code here is necessary for it to function.
      The three big issues were that

      • adjustsize() seems to not work well with scrollArea but replacing that with updateGeometry() fixed it;
      • it is necessary to override sizeHint() of scrollArea to depend on the scrollContent's sizeHint(), otherwise the scrollArea doesn't resize (second picture of previous post);
      • the label has to be shown, otherwise when manually updating the geometry of the entire ordeal it doesn't include the new label.
      1 Reply Last reply
      0
      • M Offline
        M Offline
        mpergand
        wrote on last edited by
        #2

        Can you provide a sketch of what you're trying to achieve.
        You code is a bit clumsy ;)

        A 1 Reply Last reply
        0
        • M mpergand

          Can you provide a sketch of what you're trying to achieve.
          You code is a bit clumsy ;)

          A Offline
          A Offline
          AntonBogun
          wrote on last edited by AntonBogun
          #3

          @mpergand Can you explain how is the code clumsy? Please do tell me in what way it could be made better.

          As for what I'm trying to achieve, I'm trying to make the scrollArea, and therefore the NotificationWidget, to take as much vertical space as it needs or can (if NotificationWidget is limited by MaximumHeight for example), whichever is smaller. You can see on the image the behaviour is not correct and is quite broken, that should already be a good starting point.

          M 1 Reply Last reply
          0
          • A AntonBogun

            @mpergand Can you explain how is the code clumsy? Please do tell me in what way it could be made better.

            As for what I'm trying to achieve, I'm trying to make the scrollArea, and therefore the NotificationWidget, to take as much vertical space as it needs or can (if NotificationWidget is limited by MaximumHeight for example), whichever is smaller. You can see on the image the behaviour is not correct and is quite broken, that should already be a good starting point.

            M Offline
            M Offline
            mpergand
            wrote on last edited by mpergand
            #4

            @AntonBogun
            There is nothing to the right of the list widget ?

            A 1 Reply Last reply
            0
            • M mpergand

              @AntonBogun
              There is nothing to the right of the list widget ?

              A Offline
              A Offline
              AntonBogun
              wrote on last edited by
              #5

              @mpergand I'm not sure what you mean by the "list widget" and "to the right" of it, but if you mean the scrollArea then yeah it isn't taking the entire width (300 pixels) for some reason, and also the entire height it should be able to take.

              M 1 Reply Last reply
              0
              • A AntonBogun

                @mpergand I'm not sure what you mean by the "list widget" and "to the right" of it, but if you mean the scrollArea then yeah it isn't taking the entire width (300 pixels) for some reason, and also the entire height it should be able to take.

                M Offline
                M Offline
                mpergand
                wrote on last edited by
                #6

                @AntonBogun

                	QVBoxLayout* vlayout = new QVBoxLayout(this);
                	QPushButton* collapse = new QPushButton("Collapse");
                	QPushButton* close = new QPushButton("Close");
                	QHBoxLayout* hlayout = new QHBoxLayout;
                	vlayout->addLayout(hlayout);
                	hlayout->addWidget(collapse);
                	hlayout->addWidget(close);
                
                	QScrollArea* scrollArea	= new QScrollArea;
                	scrollArea->setMaximumWidth(300);
                	vlayout->addWidget(scrollArea);
                
                

                alt text

                A 1 Reply Last reply
                0
                • M mpergand

                  @AntonBogun

                  	QVBoxLayout* vlayout = new QVBoxLayout(this);
                  	QPushButton* collapse = new QPushButton("Collapse");
                  	QPushButton* close = new QPushButton("Close");
                  	QHBoxLayout* hlayout = new QHBoxLayout;
                  	vlayout->addLayout(hlayout);
                  	hlayout->addWidget(collapse);
                  	hlayout->addWidget(close);
                  
                  	QScrollArea* scrollArea	= new QScrollArea;
                  	scrollArea->setMaximumWidth(300);
                  	vlayout->addWidget(scrollArea);
                  
                  

                  alt text

                  A Offline
                  A Offline
                  AntonBogun
                  wrote on last edited by
                  #7

                  @mpergand Sorry for the delay, but this isn't what I want because it requires setting the maximum width of the scrollArea directly
                  What is supposed to happens is this:

                  • Notification Widget: ↔ 300 fixed but no more than parent allows, ↕ as much as children need but no more than parent allows
                    • HWidget: ↔ As much as parent allows, ↕ as much as children need but no more than half the parent
                      • Buttons (stack horizontally, align to the right): ↔ As much as need, ↕ As much as need
                    • ScrollArea: ↔ As much as parent allows, ↕ as much as children need but no more than remains in parent
                      • Labels (stack vertically): ↔ As much as parent allows, ↕ as much as need, given fixed width

                  After some tweaking, I got this modified code, that seems to almost do what I want (except buttons being aligned to the right):

                  class NotificationWidget : public QWidget {
                  public:
                      int i=0;
                      NotificationWidget(QWidget *parent = nullptr) : QWidget(parent) {
                          QVBoxLayout* vlayout = new QVBoxLayout(this);
                          QPushButton* collapse = new QPushButton("Collapse");
                          QPushButton* close = new QPushButton("Close");
                          QHBoxLayout* hlayout = new QHBoxLayout;
                          vlayout->addLayout(hlayout);
                          hlayout->addWidget(collapse);
                          hlayout->addWidget(close);
                  
                          QScrollArea* scrollArea	= new QScrollArea;
                          setMaximumWidth(300);
                          vlayout->addWidget(scrollArea);
                  
                  
                          QWidget *scrollContent = new QWidget(scrollArea);
                          QVBoxLayout *scrollLayout = new QVBoxLayout(scrollContent);
                  
                          for (; i < 10; ++i) {
                              QLabel *label = new QLabel(QString("Label  %1").arg(i+1), scrollContent);
                              label->setWordWrap(1);
                              scrollLayout->addWidget(label);
                          }
                          
                          scrollContent->setLayout(scrollLayout);
                          scrollArea->setWidget(scrollContent);
                  
                          scrollArea->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
                      }
                  };
                  
                  class MainWindow : public QMainWindow {
                  public:
                      MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) {
                          QWidget *centralWidget = new QWidget(this);
                          QVBoxLayout *centralLayout = new QVBoxLayout(centralWidget);
                  
                          NotificationWidget *notificationWidget = new NotificationWidget(centralWidget);
                          notificationWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
                  
                          centralLayout->addWidget(notificationWidget);
                          centralWidget->setLayout(centralLayout);
                  
                          this->setCentralWidget(centralWidget);
                      }
                  };
                  
                  int main(int argc, char *argv[]) {
                      QApplication app(argc, argv);
                  
                      MainWindow mainWindow;
                      mainWindow.resize(800, 600);
                      mainWindow.show();
                  
                      return app.exec();
                  }
                  

                  However, when adding the ability to add new labels like this (attached to collapse button for now):

                      NotificationWidget(QWidget *parent = nullptr) : QWidget(parent) {
                          //...
                          connect(collapse,&QPushButton::clicked,[this,scrollArea,scrollLayout,scrollContent](){
                              QLabel *label = new QLabel(QString("Label  %1").arg(this->i+1), scrollContent);
                              label->setWordWrap(1);
                              scrollLayout->addWidget(label);
                              (this->i)++;
                              }
                          );
                      }
                  

                  This is what ends up happening which is the wrong behaviour:
                  5709ea59-ee49-48dd-8377-f9659c93b4ef-image.png
                  But when trying approaches like adding scrollArea->setWidgetResizable(true); it doesn't resize the whole scrollArea on adding a label meaning that the scroll bar appears:
                  f2389fd7-9dd9-40ff-94c6-acc76d95a733-image.png
                  And if I try to add something like scrollArea->adjustSize() in the label adding function, it breaks the area entirely:
                  5cf0240f-5307-4d7c-bf2e-6b2be47558c1-image.png

                  So, I currently still have no idea how to get it working correctly especially when allowing adding new labels to the scrollArea.

                  1 Reply Last reply
                  0
                  • A Offline
                    A Offline
                    AntonBogun
                    wrote on last edited by
                    #8

                    Ok, after a lot of pain and reading up on source code of updateGeometry() in QWidget and activate()/update() in QLayout, and also tracking down the issue of a new label being not shown initially (which is really weird because in another project it was fine), I got the exact behaviour I want:

                    class NotificationScrollArea : public QScrollArea {
                    public:
                        explicit NotificationScrollArea(QWidget *parent = nullptr) : QScrollArea(parent){};
                        QSize sizeHint() const override {
                            return widget()->sizeHint()+QSize(0,2);
                        }
                    };
                    
                    class NotificationWidget : public QWidget {
                    public:
                        int i=0;
                        NotificationWidget(QWidget *parent = nullptr) : QWidget(parent) {
                            QVBoxLayout* vlayout = new QVBoxLayout(this);
                            QPushButton* collapse = new QPushButton("Collapse");
                            QPushButton* close = new QPushButton("Close");
                            QHBoxLayout* hlayout = new QHBoxLayout;
                            vlayout->addLayout(hlayout);
                            hlayout->addWidget(collapse);
                            hlayout->addWidget(close);
                            hlayout->setAlignment(Qt::AlignRight);
                    
                            QScrollArea* scrollArea	= new NotificationScrollArea;
                            setMaximumWidth(300);
                            vlayout->addWidget(scrollArea);
                    
                    
                            QWidget *scrollContent = new QWidget(scrollArea);
                            QVBoxLayout *scrollLayout = new QVBoxLayout(scrollContent);
                    
                            for (; i < 10; ++i) {
                                QLabel *label = new QLabel(QString("Label  %1").arg(i+1), scrollContent);
                                label->setWordWrap(1);
                                scrollLayout->addWidget(label);
                            }
                            
                            scrollContent->setLayout(scrollLayout);
                            scrollArea->setWidget(scrollContent);
                            scrollArea->setWidgetResizable(true);
                    
                            scrollArea->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
                            connect(collapse,&QPushButton::clicked,[this,scrollArea,scrollLayout,scrollContent](){
                                i++;
                                QLabel *label = new QLabel(QString("Label  %1").arg(i), scrollContent);
                                label->setWordWrap(1);
                                scrollLayout->addWidget(label);
                                label->show();
                                scrollContent->updateGeometry();
                                scrollArea->updateGeometry();
                                updateGeometry();
                            });
                        }
                    };
                    //MainWindow and main code same as before
                    

                    I'm pretty sure every single line of code here is necessary for it to function.
                    The three big issues were that

                    • adjustsize() seems to not work well with scrollArea but replacing that with updateGeometry() fixed it;
                    • it is necessary to override sizeHint() of scrollArea to depend on the scrollContent's sizeHint(), otherwise the scrollArea doesn't resize (second picture of previous post);
                    • the label has to be shown, otherwise when manually updating the geometry of the entire ordeal it doesn't include the new label.
                    1 Reply Last reply
                    0
                    • A AntonBogun has marked this topic as solved on

                    • Login

                    • Login or register to search.
                    • First post
                      Last post
                    0
                    • Categories
                    • Recent
                    • Tags
                    • Popular
                    • Users
                    • Groups
                    • Search
                    • Get Qt Extensions
                    • Unsolved