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_OBJECTprivate:
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
-
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? :(
-