Dock widgets: Add collapse/uncollapse functionality

Add a button that can be used to collapse a dock widget's contents
temporarily, as an alternative to removing and re-adding it.

To achieve that, remove the inner widget of the dock widget and store it
in an inner pointer. Just setting the inner widget invisible/visible
does not work, because the title widget of a dock widget with invisible
inner widget becomes 0 for some reason. We also need to add a dummy
widget with a maximum height, to make the dock widget resize and not be
resizable afterwards.

Do not allow collapsing if the dock is the only one in the area, in a
horizontal layout, floating, or tabbed.

Change-Id: Ibc1acb59d58069f08184e65c4814642acb0028ee
Reviewed-by: Marcus Tillmanns <marcus.tillmanns@qt.io>
This commit is contained in:
Eike Ziller
2023-11-09 15:33:11 +01:00
parent cd6311a125
commit 09f43e79cf
3 changed files with 96 additions and 2 deletions

View File

@@ -49,9 +49,12 @@ class DockWidget : public QDockWidget
{
public:
DockWidget(QWidget *inner, FancyMainWindow *parent, bool immutable = false);
~DockWidget();
FancyMainWindow *q;
QWidget *m_hiddenInnerWidget = nullptr;
private:
QPoint m_startPos;
TitleBarWidget *m_titleBar;
@@ -130,6 +133,23 @@ public:
{
m_titleLabel = new QLabel(this);
m_collapseButton = new DockWidgetTitleButton(this);
updateCollapse();
connect(m_collapseButton, &DockWidgetTitleButton::clicked, this, [this] {
if (supportsCollapse()) {
if (!q->m_hiddenInnerWidget) {
q->m_hiddenInnerWidget = q->widget();
auto w = new QWidget;
w->setMaximumHeight(0);
q->setWidget(w);
} else {
ensureWidgetShown();
}
}
updateCollapse();
});
connect(q->q, &FancyMainWindow::dockWidgetsChanged, this, &TitleBarWidget::updateCollapse);
m_floatButton = new DockWidgetTitleButton(this);
m_floatButton->setIcon(
Icon({{":/utils/images/app-on-top.png", Theme::IconsBaseColor}}).icon());
@@ -147,6 +167,7 @@ public:
auto layout = new QHBoxLayout(this);
layout->setSpacing(0);
layout->setContentsMargins(4, 0, 0, 0);
layout->addWidget(m_collapseButton);
layout->addWidget(m_titleLabel);
layout->addStretch();
layout->addWidget(m_floatButton);
@@ -187,6 +208,7 @@ public:
&& q->features().testFlag(QDockWidget::DockWidgetFloatable));
m_closeButton->setVisible(m_active && m_hovered
&& q->features().testFlag(QDockWidget::DockWidgetClosable));
updateCollapse();
}
QSize sizeHint() const override
@@ -203,6 +225,50 @@ public:
: QSize(titleMinWidth, titleInactiveHeight);
}
bool supportsCollapse() const
{
// not if floating
if (q->isFloating())
return false;
// not if tabbed
if (!filtered(q->q->tabifiedDockWidgets(q), [](QDockWidget *w) {
return w->isVisible();
}).isEmpty())
return false;
const QList<QDockWidget *> docksInArea
= filtered(q->q->dockWidgets(), [this, area = q->q->dockWidgetArea(q)](QDockWidget *w) {
return w != q && w->isVisible() && q->q->dockWidgetArea(w) == area;
});
// not if only dock in area
if (docksInArea.isEmpty())
return false;
// not if in horizontal layout
if (anyOf(docksInArea, [y = q->y()](QDockWidget *w) { return w->y() == y; }))
return false;
return true;
}
void ensureWidgetShown()
{
if (q->m_hiddenInnerWidget) {
delete q->widget();
q->setWidget(q->m_hiddenInnerWidget);
q->m_hiddenInnerWidget = nullptr;
}
}
void updateCollapse()
{
const bool supported = m_active && supportsCollapse();
m_collapseButton->setVisible(supported);
if (!supported)
ensureWidgetShown();
if (q->m_hiddenInnerWidget)
m_collapseButton->setIcon(Icons::NEXT_TOOLBAR.icon());
else
m_collapseButton->setIcon(Icons::ARROW_DOWN_TOOLBAR.icon());
}
private:
DockWidget *q;
bool m_active = true;
@@ -210,6 +276,7 @@ private:
public:
QLabel *m_titleLabel;
DockWidgetTitleButton *m_collapseButton;
DockWidgetTitleButton *m_floatButton;
DockWidgetTitleButton *m_closeButton;
};
@@ -253,6 +320,11 @@ DockWidget::DockWidget(QWidget *inner, FancyMainWindow *parent, bool immutable)
origCloseButton, &QAbstractButton::clicked);
}
DockWidget::~DockWidget()
{
delete m_hiddenInnerWidget;
}
/*!
\class Utils::FancyMainWindow
\inmodule QtCreator
@@ -316,6 +388,19 @@ QDockWidget *FancyMainWindow::addDockForWidget(QWidget *widget, bool immutable)
}, Qt::QueuedConnection);
dockWidget->setProperty(dockWidgetActiveState, true);
connect(dockWidget,
&QDockWidget::dockLocationChanged,
this,
&FancyMainWindow::dockWidgetsChanged);
connect(dockWidget,
&QDockWidget::topLevelChanged,
this,
&FancyMainWindow::dockWidgetsChanged);
connect(dockWidget,
&QDockWidget::visibilityChanged,
this,
&FancyMainWindow::dockWidgetsChanged);
}
return dockWidget;
@@ -396,7 +481,7 @@ void FancyMainWindow::restoreSettings(const QHash<Key, QVariant> &settings)
{
QByteArray ba = settings.value(StateKey, QByteArray()).toByteArray();
if (!ba.isEmpty()) {
if (!restoreState(ba, settingsVersion))
if (!restoreFancyState(ba, settingsVersion))
qWarning() << "Restoring the state of dock widgets failed.";
}
d->m_showCentralWidget.setChecked(settings.value(ShowCentralWidgetKey, true).toBool());
@@ -406,6 +491,13 @@ void FancyMainWindow::restoreSettings(const QHash<Key, QVariant> &settings)
}
}
bool FancyMainWindow::restoreFancyState(const QByteArray &state, int version)
{
const bool result = restoreState(state, version);
emit dockWidgetsChanged();
return result;
}
static void findDockChildren(QWidget *parent, QList<QDockWidget *> &result)
{
for (QObject *child : parent->children()) {

View File

@@ -33,6 +33,7 @@ public:
void restoreSettings(const QtcSettings *settings);
QHash<Key, QVariant> saveSettings() const;
void restoreSettings(const QHash<Key, QVariant> &settings);
bool restoreFancyState(const QByteArray &state, int version = 0);
// Additional context menu actions
QAction *menuSeparator1() const;
@@ -47,6 +48,7 @@ signals:
// Emitted by resetLayoutAction(). Connect to a slot
// restoring the default layout.
void resetLayout();
void dockWidgetsChanged();
public slots:
void setDockActionsVisible(bool v);

View File

@@ -1004,7 +1004,7 @@ void PerspectivePrivate::restoreLayout()
if (state.mainWindowState.isEmpty()) {
qCDebug(perspectivesLog) << "PERSPECTIVE " << m_id << "RESTORE NOT POSSIBLE, NO STORED STATE";
} else {
bool result = theMainWindow->restoreState(state.mainWindowState);
bool result = theMainWindow->restoreFancyState(state.mainWindowState);
qCDebug(perspectivesLog) << "PERSPECTIVE " << m_id << "RESTORED, SUCCESS: " << result;
}