QDockWidget ignoring sizeHint() after user resize



  • Hi!
    I'm trying to create a collapsible dock widget, by subclassing its content widget to override the sizeHint() method.
    It almost works, but when the dock widget gets resized by the user, the size hint is no longer considered.

    Here's a minimal example (some stacked dock widgets and two push buttons to collapse/expand them):

    @
    #ifndef MYWIDGET_H
    #define MYWIDGET_H
    #include <QtWidgets/QWidget>
    #include <QtWidgets/QMainWindow>

    // Content of the dock widgets
    class MyWidget : public QWidget
    {
    Q_OBJECT

    private:
    bool isClosed;

    public slots:
    void setClosed(bool value)
    {
    if (value != isClosed)
    {
    isClosed = value;

    // sizeHint() is changed
    updateGeometry();

    // None of these works :(
    // ((QMainWindow *)(parent()))->updateGeometry();
    // ((QMainWindow *)(parent()))->adjustSize();
    }
    }

    // Wrapper
    void collapse() { setClosed(true); }
    void expand() { setClosed(false); }

    public:
    MyWidget(QWidget *parent = NULL)
    : QWidget(parent)
    {
    isClosed = false;
    }

    QSize sizeHint() const
    {
    if (isClosed)
    return QSize(0, 0);
    else
    return QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
    }
    };

    #endif
    @

    Main Window

    @
    #include "ProvaDock.h"
    #include "MyWidget.h"
    #include <QtWidgets/QDockWidget>
    #include <QtWidgets/QPushButton>
    #include <QtWidgets/QHBoxLayout>

    ProvaDock::ProvaDock(QWidget *parent)
    : QMainWindow(parent)
    {
    // Create the central widget
    QPushButton *btnCollapse = new QPushButton(tr("Collapse"));
    QPushButton *btnExpand = new QPushButton(tr("Expand"));
    QHBoxLayout *layout = new QHBoxLayout();
    layout->addWidget(btnCollapse);
    layout->addWidget(btnExpand);

    // Setup the central widget
    QWidget *contents = new QWidget();
    contents->setLayout(layout);
    setCentralWidget(contents);

    // Number of dock widgets to be added
    const unsigned int NDOCKS = 3;

    QDockWidget *lastDock = NULL;
    for (unsigned int i = 0; i < NDOCKS; i++)
    {
    // Create the dock widget
    const QString title = tr("Dock %1").arg(i + 1);
    QDockWidget *newDock = new QDockWidget(title, this);
    newDock->setWidget(new MyWidget(newDock));

    // Add the dock widget to main window
    addDockWidget(Qt::BottomDockWidgetArea, newDock, Qt::Horizontal);

    // Tabify with the last one
    if (lastDock)
    tabifyDockWidget(lastDock, newDock);

    // Signals
    QObject::connect(btnCollapse, SIGNAL(clicked()), (MyWidget *)(newDock->widget()), SLOT(collapse()));
    QObject::connect(btnExpand, SIGNAL(clicked()), (MyWidget *)(newDock->widget()), SLOT(expand()));

    // Track the last dock widget added
    lastDock = newDock;
    }

    showMaximized();
    }
    @

    I read that QMainWindow handles dock widgets with an internal layout, which unfortunately is inaccessible to the programmer.
    Is there a way to "reset" the internal state of this layout (in order to take into account the size hint again)?

    Thanks


  • Moderators

    the sizeHint - as the name says ;) - is just a hint. This size is taken into account when the Qt asks for the preferred/best size to layout/show the widget.
    So you can't use sizeHint() to control the widget's size directly.
    To do so you need to set the min/max size and/or resize the widget explicitly.



  • Ok, I spent many hours trying to get things sorted out, but I still need advice :(

    My objective is to extend the QDockWidget API by providing a method setHeight(int value) to change the dock widget's size.
    We have three elements to consider: the content widget, the dock widget and QMainWindow's internal layout, which manages docking and resizing with splitters.

    Here's the alleged internal layout behavior (for horizontally docked widgets):

    • Before user resizing (application startup):
      The splitter position is bounded by widget's minimum/maximum height, and set to be the widget's size hint if valid; if not, the minimum size is chosen.
      Dock widget resizing is completely ignored (the splitter won't move, causing the dock widget to span over the tab bar, http://i.imgur.com/BbLYIaK.jpg).

    • After user resizing:
      The splitter position is still bounded by widget's minimum/maximum height, but the actual position is memorized internally (I suppose).
      Again, dock widget resizing is completely ignored.
      Only by setting minimum=maximum you can change the splitter position, but this disables user resizing.

    Most functional solution yet:
    @
    void setHeight(int value)
    {
    QDockWidget *dockW = this;
    QMainWindow *mainW = dockW ? qobject_cast<QMainWindow *>(dockW->parent()) : NULL;

    // Set the height
    widget()->setMaximumHeight(value);
    widget()->setMinimumHeight(value);

    // Recursively update all the layouts, starting from the main window
    forceUpdate(mainW);

    // Remove the size constraint
    widget()->setMinimumHeight(QWIDGETSIZE_MAX);
    widget()->setMaximumHeight(QWIDGETSIZE_MAX);

    // Another layout update will be scheduled
    }
    @

    After user resizing, the above code works like a charm.
    Prior to that, the subsequent layout update (after the execution of setHeight()) will set the splitter's position to zero, actually undoing what setHeight() has done.

    Is there a workaround to this, or have I run out of luck? :(



  • Hello, I have the same problem. Fo you fix it?


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.