Remove the debug operator taking a const QDockWidget & argument. Leaving the override taking a pointer. This amends bbeff2a3350dd3396400865525d509b784c2d93e. Change-Id: I0fbca6ea7dbffe6269c70e5e9eb29af9f84c3600 Found-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> Task-number: QTBUG-119952 Reviewed-by: Santhosh Kumar <santhosh.kumar.selvaraj@qt.io> Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io> (cherry picked from commit ca2f46c04c26ed4649cb6c2c62d3b2e52cd8d5ad) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> (cherry picked from commit f83ba9d3cf80512fa11dff12ba932c457952c9cf)
1866 lines
60 KiB
C++
1866 lines
60 KiB
C++
// Copyright (C) 2016 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
|
|
#include "qdockwidget.h"
|
|
|
|
#include <qaction.h>
|
|
#include <qapplication.h>
|
|
#include <qdrawutil.h>
|
|
#include <qevent.h>
|
|
#include <qfontmetrics.h>
|
|
#include <qproxystyle.h>
|
|
#include <qwindow.h>
|
|
#include <qscreen.h>
|
|
#include <qmainwindow.h>
|
|
#include <qstylepainter.h>
|
|
#include <qtoolbutton.h>
|
|
#include <qdebug.h>
|
|
|
|
#include <private/qwidgetresizehandler_p.h>
|
|
#include <private/qstylesheetstyle_p.h>
|
|
#include <qpa/qplatformtheme.h>
|
|
|
|
#include <private/qhighdpiscaling_p.h>
|
|
#include "qdockwidget_p.h"
|
|
#include "qmainwindowlayout_p.h"
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
using namespace Qt::StringLiterals;
|
|
|
|
extern QString qt_setWindowTitle_helperHelper(const QString&, const QWidget*); // qwidget.cpp
|
|
|
|
// qmainwindow.cpp
|
|
extern QMainWindowLayout *qt_mainwindow_layout(const QMainWindow *window);
|
|
|
|
static const QMainWindow *mainwindow_from_dock(const QDockWidget *dock)
|
|
{
|
|
for (const QWidget *p = dock->parentWidget(); p; p = p->parentWidget()) {
|
|
if (const QMainWindow *window = qobject_cast<const QMainWindow*>(p))
|
|
return window;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static inline QMainWindowLayout *qt_mainwindow_layout_from_dock(const QDockWidget *dock)
|
|
{
|
|
auto mainWindow = mainwindow_from_dock(dock);
|
|
return mainWindow ? qt_mainwindow_layout(mainWindow) : nullptr;
|
|
}
|
|
|
|
static inline bool hasFeature(const QDockWidgetPrivate *priv, QDockWidget::DockWidgetFeature feature)
|
|
{ return (priv->features & feature) == feature; }
|
|
|
|
static inline bool hasFeature(const QDockWidget *dockwidget, QDockWidget::DockWidgetFeature feature)
|
|
{ return (dockwidget->features() & feature) == feature; }
|
|
|
|
|
|
/*
|
|
A Dock Window:
|
|
|
|
[+] is the float button
|
|
[X] is the close button
|
|
|
|
+-------------------------------+
|
|
| Dock Window Title [+][X]|
|
|
+-------------------------------+
|
|
| |
|
|
| place to put the single |
|
|
| QDockWidget child (this space |
|
|
| does not yet have a name) |
|
|
| |
|
|
| |
|
|
| |
|
|
| |
|
|
| |
|
|
| |
|
|
| |
|
|
| |
|
|
| |
|
|
+-------------------------------+
|
|
|
|
*/
|
|
|
|
/******************************************************************************
|
|
** QDockWidgetTitleButton
|
|
*/
|
|
|
|
class QDockWidgetTitleButton : public QAbstractButton
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
QDockWidgetTitleButton(QDockWidget *dockWidget);
|
|
|
|
QSize sizeHint() const override;
|
|
QSize minimumSizeHint() const override
|
|
{ return sizeHint(); }
|
|
|
|
void enterEvent(QEnterEvent *event) override;
|
|
void leaveEvent(QEvent *event) override;
|
|
void paintEvent(QPaintEvent *event) override;
|
|
|
|
protected:
|
|
bool event(QEvent *event) override;
|
|
|
|
private:
|
|
QSize dockButtonIconSize() const;
|
|
|
|
mutable int m_iconSize = -1;
|
|
};
|
|
|
|
QDockWidgetTitleButton::QDockWidgetTitleButton(QDockWidget *dockWidget)
|
|
: QAbstractButton(dockWidget)
|
|
{
|
|
setFocusPolicy(Qt::NoFocus);
|
|
}
|
|
|
|
bool QDockWidgetTitleButton::event(QEvent *event)
|
|
{
|
|
switch (event->type()) {
|
|
case QEvent::StyleChange:
|
|
case QEvent::DevicePixelRatioChange:
|
|
m_iconSize = -1;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return QAbstractButton::event(event);
|
|
}
|
|
|
|
static inline bool isWindowsStyle(const QStyle *style)
|
|
{
|
|
// Note: QStyleSheetStyle inherits QWindowsStyle
|
|
const QStyle *effectiveStyle = style;
|
|
|
|
#if QT_CONFIG(style_stylesheet)
|
|
if (style->inherits("QStyleSheetStyle"))
|
|
effectiveStyle = static_cast<const QStyleSheetStyle *>(style)->baseStyle();
|
|
#endif
|
|
#if !defined(QT_NO_STYLE_PROXY)
|
|
if (style->inherits("QProxyStyle"))
|
|
effectiveStyle = static_cast<const QProxyStyle *>(style)->baseStyle();
|
|
#endif
|
|
|
|
return effectiveStyle->inherits("QWindowsStyle");
|
|
}
|
|
|
|
QSize QDockWidgetTitleButton::dockButtonIconSize() const
|
|
{
|
|
if (m_iconSize < 0) {
|
|
m_iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, this);
|
|
// Dock Widget title buttons on Windows where historically limited to size 10
|
|
// (from small icon size 16) since only a 10x10 XPM was provided.
|
|
// Adding larger pixmaps to the icons thus caused the icons to grow; limit
|
|
// this to qpiScaled(10) here.
|
|
if (isWindowsStyle(style()))
|
|
m_iconSize = qMin((10 * logicalDpiX()) / 96, m_iconSize);
|
|
}
|
|
return QSize(m_iconSize, m_iconSize);
|
|
}
|
|
|
|
QSize QDockWidgetTitleButton::sizeHint() const
|
|
{
|
|
ensurePolished();
|
|
|
|
int size = 2*style()->pixelMetric(QStyle::PM_DockWidgetTitleBarButtonMargin, nullptr, this);
|
|
if (!icon().isNull()) {
|
|
const QSize sz = icon().actualSize(dockButtonIconSize());
|
|
size += qMax(sz.width(), sz.height());
|
|
}
|
|
|
|
return QSize(size, size);
|
|
}
|
|
|
|
void QDockWidgetTitleButton::enterEvent(QEnterEvent *event)
|
|
{
|
|
if (isEnabled()) update();
|
|
QAbstractButton::enterEvent(event);
|
|
}
|
|
|
|
void QDockWidgetTitleButton::leaveEvent(QEvent *event)
|
|
{
|
|
if (isEnabled()) update();
|
|
QAbstractButton::leaveEvent(event);
|
|
}
|
|
|
|
void QDockWidgetTitleButton::paintEvent(QPaintEvent *)
|
|
{
|
|
QStylePainter p(this);
|
|
|
|
QStyleOptionToolButton opt;
|
|
opt.initFrom(this);
|
|
opt.state |= QStyle::State_AutoRaise;
|
|
|
|
if (style()->styleHint(QStyle::SH_DockWidget_ButtonsHaveFrame, nullptr, this)) {
|
|
if (isEnabled() && underMouse() && !isChecked() && !isDown())
|
|
opt.state |= QStyle::State_Raised;
|
|
if (isChecked())
|
|
opt.state |= QStyle::State_On;
|
|
if (isDown())
|
|
opt.state |= QStyle::State_Sunken;
|
|
p.drawPrimitive(QStyle::PE_PanelButtonTool, opt);
|
|
} else if (isDown() || isChecked()) {
|
|
// no frame, but the icon might have explicit pixmaps for QIcon::On
|
|
opt.state |= QStyle::State_On | QStyle::State_Sunken;
|
|
}
|
|
|
|
opt.icon = icon();
|
|
opt.subControls = { };
|
|
opt.activeSubControls = { };
|
|
opt.features = QStyleOptionToolButton::None;
|
|
opt.arrowType = Qt::NoArrow;
|
|
opt.iconSize = dockButtonIconSize();
|
|
p.drawComplexControl(QStyle::CC_ToolButton, opt);
|
|
}
|
|
|
|
/******************************************************************************
|
|
** QDockWidgetLayout
|
|
*/
|
|
|
|
QDockWidgetLayout::QDockWidgetLayout(QWidget *parent)
|
|
: QLayout(parent), verticalTitleBar(false), item_list(RoleCount, 0)
|
|
{
|
|
}
|
|
|
|
QDockWidgetLayout::~QDockWidgetLayout()
|
|
{
|
|
qDeleteAll(item_list);
|
|
}
|
|
|
|
/*! \internal
|
|
Returns true if the dock widget managed by this layout should have a native
|
|
window decoration or if Qt needs to draw it.
|
|
*/
|
|
bool QDockWidgetLayout::nativeWindowDeco() const
|
|
{
|
|
bool floating = parentWidget()->isWindow();
|
|
#if QT_CONFIG(tabbar)
|
|
if (auto groupWindow =
|
|
qobject_cast<const QDockWidgetGroupWindow *>(parentWidget()->parentWidget()))
|
|
floating = floating || groupWindow->tabLayoutInfo();
|
|
#endif
|
|
return nativeWindowDeco(floating);
|
|
}
|
|
|
|
/*! \internal
|
|
Returns true if the window manager can draw natively the windows decoration
|
|
of a dock widget
|
|
*/
|
|
bool QDockWidgetLayout::wmSupportsNativeWindowDeco()
|
|
{
|
|
#if defined(Q_OS_ANDROID)
|
|
return false;
|
|
#else
|
|
static const bool xcb = !QGuiApplication::platformName().compare("xcb"_L1, Qt::CaseInsensitive);
|
|
static const bool wayland =
|
|
QGuiApplication::platformName().startsWith("wayland"_L1, Qt::CaseInsensitive);
|
|
return !(xcb || wayland);
|
|
#endif
|
|
}
|
|
|
|
/*! \internal
|
|
Returns true if the dock widget managed by this layout should have a native
|
|
window decoration or if Qt needs to draw it. The \a floating parameter
|
|
overrides the floating current state of the dock widget.
|
|
*/
|
|
bool QDockWidgetLayout::nativeWindowDeco(bool floating) const
|
|
{
|
|
return wmSupportsNativeWindowDeco() && floating && item_list.at(QDockWidgetLayout::TitleBar) == nullptr;
|
|
}
|
|
|
|
|
|
void QDockWidgetLayout::addItem(QLayoutItem*)
|
|
{
|
|
qWarning("QDockWidgetLayout::addItem(): please use QDockWidgetLayout::setWidget()");
|
|
return;
|
|
}
|
|
|
|
QLayoutItem *QDockWidgetLayout::itemAt(int index) const
|
|
{
|
|
int cnt = 0;
|
|
for (int i = 0; i < item_list.size(); ++i) {
|
|
QLayoutItem *item = item_list.at(i);
|
|
if (item == nullptr)
|
|
continue;
|
|
if (index == cnt++)
|
|
return item;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
QLayoutItem *QDockWidgetLayout::takeAt(int index)
|
|
{
|
|
int j = 0;
|
|
for (int i = 0; i < item_list.size(); ++i) {
|
|
QLayoutItem *item = item_list.at(i);
|
|
if (item == nullptr)
|
|
continue;
|
|
if (index == j) {
|
|
item_list[i] = 0;
|
|
invalidate();
|
|
return item;
|
|
}
|
|
++j;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
int QDockWidgetLayout::count() const
|
|
{
|
|
int result = 0;
|
|
for (int i = 0; i < item_list.size(); ++i) {
|
|
if (item_list.at(i))
|
|
++result;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
QSize QDockWidgetLayout::sizeFromContent(const QSize &content, bool floating) const
|
|
{
|
|
QSize result = content;
|
|
|
|
if (verticalTitleBar) {
|
|
result.setHeight(qMax(result.height(), minimumTitleWidth()));
|
|
result.setWidth(qMax(content.width(), 0));
|
|
} else {
|
|
result.setHeight(qMax(result.height(), 0));
|
|
result.setWidth(qMax(content.width(), minimumTitleWidth()));
|
|
}
|
|
|
|
QDockWidget *w = qobject_cast<QDockWidget*>(parentWidget());
|
|
const bool nativeDeco = nativeWindowDeco(floating);
|
|
|
|
int fw = floating && !nativeDeco
|
|
? w->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, nullptr, w)
|
|
: 0;
|
|
|
|
const int th = titleHeight();
|
|
if (!nativeDeco) {
|
|
if (verticalTitleBar)
|
|
result += QSize(th + 2*fw, 2*fw);
|
|
else
|
|
result += QSize(2*fw, th + 2*fw);
|
|
}
|
|
|
|
result.setHeight(qMin(result.height(), (int) QWIDGETSIZE_MAX));
|
|
result.setWidth(qMin(result.width(), (int) QWIDGETSIZE_MAX));
|
|
|
|
if (content.width() < 0)
|
|
result.setWidth(-1);
|
|
if (content.height() < 0)
|
|
result.setHeight(-1);
|
|
|
|
const QMargins margins = w->contentsMargins();
|
|
//we need to subtract the contents margin (it will be added by the caller)
|
|
QSize min = w->minimumSize().shrunkBy(margins);
|
|
QSize max = w->maximumSize().shrunkBy(margins);
|
|
|
|
/* A floating dockwidget will automatically get its minimumSize set to the layout's
|
|
minimum size + deco. We're *not* interested in this, we only take minimumSize()
|
|
into account if the user set it herself. Otherwise we end up expanding the result
|
|
of a calculation for a non-floating dock widget to a floating dock widget's
|
|
minimum size + window decorations. */
|
|
|
|
uint explicitMin = 0;
|
|
uint explicitMax = 0;
|
|
if (w->d_func()->extra != nullptr) {
|
|
explicitMin = w->d_func()->extra->explicitMinSize;
|
|
explicitMax = w->d_func()->extra->explicitMaxSize;
|
|
}
|
|
|
|
if (!(explicitMin & Qt::Horizontal) || min.width() == 0)
|
|
min.setWidth(-1);
|
|
if (!(explicitMin & Qt::Vertical) || min.height() == 0)
|
|
min.setHeight(-1);
|
|
|
|
if (!(explicitMax & Qt::Horizontal))
|
|
max.setWidth(QWIDGETSIZE_MAX);
|
|
if (!(explicitMax & Qt::Vertical))
|
|
max.setHeight(QWIDGETSIZE_MAX);
|
|
|
|
return result.boundedTo(max).expandedTo(min);
|
|
}
|
|
|
|
QSize QDockWidgetLayout::sizeHint() const
|
|
{
|
|
QDockWidget *w = qobject_cast<QDockWidget*>(parentWidget());
|
|
|
|
QSize content(-1, -1);
|
|
if (item_list[Content] != 0)
|
|
content = item_list[Content]->sizeHint();
|
|
|
|
return sizeFromContent(content, w->isFloating());
|
|
}
|
|
|
|
QSize QDockWidgetLayout::maximumSize() const
|
|
{
|
|
if (item_list[Content] != 0) {
|
|
const QSize content = item_list[Content]->maximumSize();
|
|
return sizeFromContent(content, parentWidget()->isWindow());
|
|
} else {
|
|
return parentWidget()->maximumSize();
|
|
}
|
|
|
|
}
|
|
|
|
QSize QDockWidgetLayout::minimumSize() const
|
|
{
|
|
QDockWidget *w = qobject_cast<QDockWidget*>(parentWidget());
|
|
|
|
QSize content(0, 0);
|
|
if (item_list[Content] != 0)
|
|
content = item_list[Content]->minimumSize();
|
|
|
|
return sizeFromContent(content, w->isFloating());
|
|
}
|
|
|
|
QWidget *QDockWidgetLayout::widgetForRole(Role r) const
|
|
{
|
|
QLayoutItem *item = item_list.at(r);
|
|
return item == nullptr ? nullptr : item->widget();
|
|
}
|
|
|
|
QLayoutItem *QDockWidgetLayout::itemForRole(Role r) const
|
|
{
|
|
return item_list.at(r);
|
|
}
|
|
|
|
void QDockWidgetLayout::setWidgetForRole(Role r, QWidget *w)
|
|
{
|
|
QWidget *old = widgetForRole(r);
|
|
if (old != nullptr) {
|
|
old->hide();
|
|
removeWidget(old);
|
|
}
|
|
|
|
if (w != nullptr) {
|
|
addChildWidget(w);
|
|
item_list[r] = new QWidgetItemV2(w);
|
|
w->show();
|
|
} else {
|
|
item_list[r] = 0;
|
|
}
|
|
|
|
invalidate();
|
|
}
|
|
|
|
static inline int pick(bool vertical, const QSize &size)
|
|
{
|
|
return vertical ? size.height() : size.width();
|
|
}
|
|
|
|
static inline int perp(bool vertical, const QSize &size)
|
|
{
|
|
return vertical ? size.width() : size.height();
|
|
}
|
|
|
|
int QDockWidgetLayout::minimumTitleWidth() const
|
|
{
|
|
QDockWidget *q = qobject_cast<QDockWidget*>(parentWidget());
|
|
|
|
if (QWidget *title = widgetForRole(TitleBar))
|
|
return pick(verticalTitleBar, title->minimumSizeHint());
|
|
|
|
QSize closeSize(0, 0);
|
|
QSize floatSize(0, 0);
|
|
if (hasFeature(q, QDockWidget::DockWidgetClosable)) {
|
|
if (QLayoutItem *item = item_list[CloseButton])
|
|
closeSize = item->widget()->sizeHint();
|
|
}
|
|
if (hasFeature(q, QDockWidget::DockWidgetFloatable)) {
|
|
if (QLayoutItem *item = item_list[FloatButton])
|
|
floatSize = item->widget()->sizeHint();
|
|
}
|
|
|
|
int titleHeight = this->titleHeight();
|
|
|
|
int mw = q->style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, nullptr, q);
|
|
int fw = q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, nullptr, q);
|
|
|
|
return pick(verticalTitleBar, closeSize)
|
|
+ pick(verticalTitleBar, floatSize)
|
|
+ titleHeight + 2*fw + 3*mw;
|
|
}
|
|
|
|
int QDockWidgetLayout::titleHeight() const
|
|
{
|
|
QDockWidget *q = qobject_cast<QDockWidget*>(parentWidget());
|
|
|
|
if (QWidget *title = widgetForRole(TitleBar))
|
|
return perp(verticalTitleBar, title->sizeHint());
|
|
|
|
QSize closeSize(0, 0);
|
|
QSize floatSize(0, 0);
|
|
if (QLayoutItem *item = item_list[CloseButton])
|
|
closeSize = item->widget()->sizeHint();
|
|
if (QLayoutItem *item = item_list[FloatButton])
|
|
floatSize = item->widget()->sizeHint();
|
|
|
|
int buttonHeight = qMax(perp(verticalTitleBar, closeSize),
|
|
perp(verticalTitleBar, floatSize));
|
|
|
|
QFontMetrics titleFontMetrics = q->fontMetrics();
|
|
int mw = q->style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, nullptr, q);
|
|
|
|
return qMax(buttonHeight + 2, titleFontMetrics.height() + 2*mw);
|
|
}
|
|
|
|
void QDockWidgetLayout::setGeometry(const QRect &geometry)
|
|
{
|
|
QDockWidget *q = qobject_cast<QDockWidget*>(parentWidget());
|
|
|
|
bool nativeDeco = nativeWindowDeco();
|
|
|
|
int fw = q->isFloating() && !nativeDeco
|
|
? q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, nullptr, q)
|
|
: 0;
|
|
|
|
if (nativeDeco) {
|
|
if (QLayoutItem *item = item_list[Content])
|
|
item->setGeometry(geometry);
|
|
} else {
|
|
int titleHeight = this->titleHeight();
|
|
|
|
if (verticalTitleBar) {
|
|
_titleArea = QRect(QPoint(fw, fw),
|
|
QSize(titleHeight, geometry.height() - (fw * 2)));
|
|
} else {
|
|
_titleArea = QRect(QPoint(fw, fw),
|
|
QSize(geometry.width() - (fw * 2), titleHeight));
|
|
}
|
|
|
|
if (QLayoutItem *item = item_list[TitleBar]) {
|
|
item->setGeometry(_titleArea);
|
|
} else {
|
|
QStyleOptionDockWidget opt;
|
|
q->initStyleOption(&opt);
|
|
|
|
if (QLayoutItem *item = item_list[CloseButton]) {
|
|
if (!item->isEmpty()) {
|
|
QRect r = q->style()
|
|
->subElementRect(QStyle::SE_DockWidgetCloseButton,
|
|
&opt, q);
|
|
if (!r.isNull())
|
|
item->setGeometry(r);
|
|
}
|
|
}
|
|
|
|
if (QLayoutItem *item = item_list[FloatButton]) {
|
|
if (!item->isEmpty()) {
|
|
QRect r = q->style()
|
|
->subElementRect(QStyle::SE_DockWidgetFloatButton,
|
|
&opt, q);
|
|
if (!r.isNull())
|
|
item->setGeometry(r);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (QLayoutItem *item = item_list[Content]) {
|
|
QRect r = geometry;
|
|
if (verticalTitleBar) {
|
|
r.setLeft(_titleArea.right() + 1);
|
|
r.adjust(0, fw, -fw, -fw);
|
|
} else {
|
|
r.setTop(_titleArea.bottom() + 1);
|
|
r.adjust(fw, 0, -fw, -fw);
|
|
}
|
|
item->setGeometry(r);
|
|
}
|
|
}
|
|
}
|
|
|
|
void QDockWidgetLayout::setVerticalTitleBar(bool b)
|
|
{
|
|
if (b == verticalTitleBar)
|
|
return;
|
|
verticalTitleBar = b;
|
|
invalidate();
|
|
parentWidget()->update();
|
|
}
|
|
|
|
/******************************************************************************
|
|
** QDockWidgetItem
|
|
*/
|
|
|
|
QDockWidgetItem::QDockWidgetItem(QDockWidget *dockWidget)
|
|
: QWidgetItem(dockWidget)
|
|
{
|
|
}
|
|
|
|
QSize QDockWidgetItem::minimumSize() const
|
|
{
|
|
QSize widgetMin(0, 0);
|
|
if (QLayoutItem *item = dockWidgetChildItem())
|
|
widgetMin = item->minimumSize();
|
|
return dockWidgetLayout()->sizeFromContent(widgetMin, false);
|
|
}
|
|
|
|
QSize QDockWidgetItem::maximumSize() const
|
|
{
|
|
if (QLayoutItem *item = dockWidgetChildItem()) {
|
|
return dockWidgetLayout()->sizeFromContent(item->maximumSize(), false);
|
|
} else {
|
|
return QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
|
|
}
|
|
}
|
|
|
|
|
|
QSize QDockWidgetItem::sizeHint() const
|
|
{
|
|
if (QLayoutItem *item = dockWidgetChildItem()) {
|
|
return dockWidgetLayout()->sizeFromContent(item->sizeHint(), false);
|
|
} else {
|
|
return QWidgetItem::sizeHint();
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
** QDockWidgetPrivate
|
|
*/
|
|
|
|
void QDockWidgetPrivate::init()
|
|
{
|
|
Q_Q(QDockWidget);
|
|
|
|
QDockWidgetLayout *layout = new QDockWidgetLayout(q);
|
|
layout->setSizeConstraint(QLayout::SetMinAndMaxSize);
|
|
|
|
QAbstractButton *button = new QDockWidgetTitleButton(q);
|
|
button->setObjectName("qt_dockwidget_floatbutton"_L1);
|
|
QObjectPrivate::connect(button, &QAbstractButton::clicked,
|
|
this, &QDockWidgetPrivate::toggleTopLevel);
|
|
layout->setWidgetForRole(QDockWidgetLayout::FloatButton, button);
|
|
|
|
button = new QDockWidgetTitleButton(q);
|
|
button->setObjectName("qt_dockwidget_closebutton"_L1);
|
|
QObject::connect(button, &QAbstractButton::clicked, q, &QDockWidget::close);
|
|
layout->setWidgetForRole(QDockWidgetLayout::CloseButton, button);
|
|
|
|
font = QApplication::font("QDockWidgetTitle");
|
|
|
|
#ifndef QT_NO_ACTION
|
|
toggleViewAction = new QAction(q);
|
|
toggleViewAction->setCheckable(true);
|
|
toggleViewAction->setMenuRole(QAction::NoRole);
|
|
fixedWindowTitle = qt_setWindowTitle_helperHelper(q->windowTitle(), q);
|
|
toggleViewAction->setText(fixedWindowTitle);
|
|
QObjectPrivate::connect(toggleViewAction, &QAction::triggered,
|
|
this, &QDockWidgetPrivate::toggleView);
|
|
#endif
|
|
|
|
updateButtons();
|
|
}
|
|
|
|
/*!
|
|
Initialize \a option with the values from this QDockWidget. This method
|
|
is useful for subclasses when they need a QStyleOptionDockWidget, but don't want
|
|
to fill in all the information themselves.
|
|
|
|
\sa QStyleOption::initFrom()
|
|
*/
|
|
void QDockWidget::initStyleOption(QStyleOptionDockWidget *option) const
|
|
{
|
|
Q_D(const QDockWidget);
|
|
|
|
if (!option)
|
|
return;
|
|
QDockWidgetLayout *dwlayout = qobject_cast<QDockWidgetLayout*>(layout());
|
|
|
|
QDockWidgetGroupWindow *floatingTab = qobject_cast<QDockWidgetGroupWindow*>(parent());
|
|
// If we are in a floating tab, init from the parent because the attributes and the geometry
|
|
// of the title bar should be taken from the floating window.
|
|
option->initFrom(floatingTab && !isFloating() ? parentWidget() : this);
|
|
option->rect = dwlayout->titleArea();
|
|
option->title = d->fixedWindowTitle;
|
|
option->closable = hasFeature(this, QDockWidget::DockWidgetClosable);
|
|
option->movable = hasFeature(this, QDockWidget::DockWidgetMovable);
|
|
option->floatable = hasFeature(this, QDockWidget::DockWidgetFloatable);
|
|
|
|
QDockWidgetLayout *l = qobject_cast<QDockWidgetLayout*>(layout());
|
|
option->verticalTitleBar = l->verticalTitleBar;
|
|
}
|
|
|
|
void QDockWidgetPrivate::toggleView(bool b)
|
|
{
|
|
Q_Q(QDockWidget);
|
|
if (b == q->isHidden()) {
|
|
if (b)
|
|
q->show();
|
|
else
|
|
q->close();
|
|
}
|
|
}
|
|
|
|
void QDockWidgetPrivate::updateButtons()
|
|
{
|
|
Q_Q(QDockWidget);
|
|
QDockWidgetLayout *dwLayout = qobject_cast<QDockWidgetLayout*>(layout);
|
|
|
|
QStyleOptionDockWidget opt;
|
|
q->initStyleOption(&opt);
|
|
|
|
bool customTitleBar = dwLayout->widgetForRole(QDockWidgetLayout::TitleBar) != nullptr;
|
|
bool nativeDeco = dwLayout->nativeWindowDeco();
|
|
bool hideButtons = nativeDeco || customTitleBar;
|
|
|
|
bool canClose = hasFeature(this, QDockWidget::DockWidgetClosable);
|
|
bool canFloat = hasFeature(this, QDockWidget::DockWidgetFloatable);
|
|
|
|
QAbstractButton *button
|
|
= qobject_cast<QAbstractButton*>(dwLayout->widgetForRole(QDockWidgetLayout::FloatButton));
|
|
button->setIcon(q->style()->standardIcon(QStyle::SP_TitleBarNormalButton, &opt, q));
|
|
button->setVisible(canFloat && !hideButtons);
|
|
#if QT_CONFIG(accessibility)
|
|
//: Accessible name for button undocking a dock widget (floating state)
|
|
button->setAccessibleName(QDockWidget::tr("Float"));
|
|
button->setAccessibleDescription(QDockWidget::tr("Undocks and re-attaches the dock widget"));
|
|
#endif
|
|
button
|
|
= qobject_cast <QAbstractButton*>(dwLayout->widgetForRole(QDockWidgetLayout::CloseButton));
|
|
button->setIcon(q->style()->standardIcon(QStyle::SP_TitleBarCloseButton, &opt, q));
|
|
button->setVisible(canClose && !hideButtons);
|
|
#if QT_CONFIG(accessibility)
|
|
//: Accessible name for button closing a dock widget
|
|
button->setAccessibleName(QDockWidget::tr("Close"));
|
|
button->setAccessibleDescription(QDockWidget::tr("Closes the dock widget"));
|
|
#endif
|
|
|
|
layout->invalidate();
|
|
}
|
|
|
|
void QDockWidgetPrivate::toggleTopLevel()
|
|
{
|
|
Q_Q(QDockWidget);
|
|
q->setFloating(!q->isFloating());
|
|
}
|
|
|
|
/*! \internal
|
|
Initialize the drag state structure and remember the position of the click.
|
|
This is called when the mouse is pressed, but the dock is not yet dragged out.
|
|
|
|
\a nca specify that the event comes from NonClientAreaMouseButtonPress
|
|
*/
|
|
void QDockWidgetPrivate::initDrag(const QPoint &pos, bool nca)
|
|
{
|
|
Q_Q(QDockWidget);
|
|
|
|
if (state != nullptr)
|
|
return;
|
|
|
|
QMainWindowLayout *layout = qt_mainwindow_layout_from_dock(q);
|
|
Q_ASSERT(layout != nullptr);
|
|
if (layout->pluggingWidget != nullptr) // the main window is animating a docking operation
|
|
return;
|
|
|
|
state = new QDockWidgetPrivate::DragState;
|
|
state->pressPos = pos;
|
|
state->globalPressPos = q->mapToGlobal(pos);
|
|
state->widgetInitialPos = q->isFloating() ? q->pos() : q->mapToGlobal(QPoint(0, 0));
|
|
state->dragging = false;
|
|
state->widgetItem = nullptr;
|
|
state->ownWidgetItem = false;
|
|
state->nca = nca;
|
|
state->ctrlDrag = false;
|
|
}
|
|
|
|
/*! \internal
|
|
Actually start the drag and detach the dockwidget.
|
|
The \a group parameter is true when we should potentially drag a group of
|
|
tabbed widgets, and false if the dock widget should always be dragged
|
|
alone.
|
|
*/
|
|
void QDockWidgetPrivate::startDrag(DragScope scope)
|
|
{
|
|
Q_Q(QDockWidget);
|
|
|
|
if (state == nullptr || state->dragging)
|
|
return;
|
|
|
|
QMainWindowLayout *layout = qt_mainwindow_layout_from_dock(q);
|
|
Q_ASSERT(layout != nullptr);
|
|
|
|
bool wasFloating = q->isFloating();
|
|
|
|
state->widgetItem = layout->unplug(q, scope);
|
|
if (state->widgetItem == nullptr) {
|
|
/* Dock widget has a QMainWindow parent, but was never inserted with
|
|
QMainWindow::addDockWidget, so the QMainWindowLayout has no
|
|
widget item for it. It will be newly created and deleted if it doesn't
|
|
get dropped into a dock area. */
|
|
QDockWidgetGroupWindow *floatingTab = qobject_cast<QDockWidgetGroupWindow*>(parent);
|
|
if (floatingTab && !q->isFloating())
|
|
state->widgetItem = new QDockWidgetGroupWindowItem(floatingTab);
|
|
else
|
|
state->widgetItem = new QDockWidgetItem(q);
|
|
state->ownWidgetItem = true;
|
|
}
|
|
|
|
if (state->ctrlDrag)
|
|
layout->restore();
|
|
|
|
state->dragging = true;
|
|
|
|
#if QT_CONFIG(draganddrop)
|
|
if (QMainWindowLayout::needsPlatformDrag()) {
|
|
Qt::DropAction result =
|
|
layout->performPlatformWidgetDrag(state->widgetItem, state->pressPos);
|
|
if (result == Qt::IgnoreAction && !wasFloating) {
|
|
layout->revert(state->widgetItem);
|
|
delete state;
|
|
state = nullptr;
|
|
} else {
|
|
endDrag(QDockWidgetPrivate::EndDragMode::LocationChange);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*! \internal
|
|
Ends the drag end drop operation of the QDockWidget.
|
|
The \a abort parameter specifies that it ends because of programmatic state
|
|
reset rather than mouse release event.
|
|
*/
|
|
void QDockWidgetPrivate::endDrag(EndDragMode mode)
|
|
{
|
|
Q_Q(QDockWidget);
|
|
Q_ASSERT(state != nullptr);
|
|
|
|
q->releaseMouse();
|
|
|
|
if (state->dragging) {
|
|
const QMainWindow *mainWindow = mainwindow_from_dock(q);
|
|
Q_ASSERT(mainWindow != nullptr);
|
|
QMainWindowLayout *mwLayout = qt_mainwindow_layout(mainWindow);
|
|
|
|
// if mainWindow is being deleted in an ongoing drag, make it a no-op instead of crashing
|
|
if (!mwLayout)
|
|
return;
|
|
|
|
if (mode == EndDragMode::Abort || !mwLayout->plug(state->widgetItem)) {
|
|
if (hasFeature(this, QDockWidget::DockWidgetFloatable)) {
|
|
// This QDockWidget will now stay in the floating state.
|
|
if (state->ownWidgetItem) {
|
|
delete state->widgetItem;
|
|
state->widgetItem = nullptr;
|
|
}
|
|
mwLayout->restore();
|
|
QDockWidgetLayout *dwLayout = qobject_cast<QDockWidgetLayout*>(layout);
|
|
if (!dwLayout->nativeWindowDeco()) {
|
|
// get rid of the X11BypassWindowManager window flag and activate the resizer
|
|
Qt::WindowFlags flags = q->windowFlags();
|
|
flags &= ~Qt::X11BypassWindowManagerHint;
|
|
q->setWindowFlags(flags);
|
|
setResizerActive(q->isFloating());
|
|
q->show();
|
|
} else {
|
|
setResizerActive(false);
|
|
}
|
|
if (q->isFloating()) { // Might not be floating when dragging a QDockWidgetGroupWindow
|
|
undockedGeometry = q->geometry();
|
|
#if QT_CONFIG(tabwidget)
|
|
// is the widget located within the mainwindow?
|
|
const Qt::DockWidgetArea area = mainWindow->dockWidgetArea(q);
|
|
if (area != Qt::NoDockWidgetArea) {
|
|
tabPosition = mwLayout->tabPosition(area);
|
|
} else if (auto dwgw = qobject_cast<QDockWidgetGroupWindow *>(q->parent())) {
|
|
// DockWidget wasn't found in one of the docks within mainwindow
|
|
// => derive tabPosition from parent
|
|
tabPosition = mwLayout->tabPosition(toDockWidgetArea(dwgw->layoutInfo()->dockPos));
|
|
}
|
|
#endif
|
|
// Reparent, if the drag was out of a dock widget group window
|
|
if (mode == EndDragMode::LocationChange) {
|
|
if (auto *groupWindow = qobject_cast<QDockWidgetGroupWindow *>(q->parentWidget()))
|
|
groupWindow->reparent(q);
|
|
}
|
|
}
|
|
q->activateWindow();
|
|
} else {
|
|
// The tab was not plugged back in the QMainWindow but the QDockWidget cannot
|
|
// stay floating, revert to the previous state.
|
|
mwLayout->revert(state->widgetItem);
|
|
}
|
|
}
|
|
}
|
|
delete state;
|
|
state = nullptr;
|
|
}
|
|
|
|
Qt::DockWidgetArea QDockWidgetPrivate::toDockWidgetArea(QInternal::DockPosition pos)
|
|
{
|
|
switch (pos) {
|
|
case QInternal::LeftDock: return Qt::LeftDockWidgetArea;
|
|
case QInternal::RightDock: return Qt::RightDockWidgetArea;
|
|
case QInternal::TopDock: return Qt::TopDockWidgetArea;
|
|
case QInternal::BottomDock: return Qt::BottomDockWidgetArea;
|
|
default: break;
|
|
}
|
|
return Qt::NoDockWidgetArea;
|
|
}
|
|
|
|
void QDockWidgetPrivate::setResizerActive(bool active)
|
|
{
|
|
#ifdef Q_OS_WINDOWS
|
|
Q_UNUSED(active);
|
|
#else
|
|
Q_Q(QDockWidget);
|
|
if (active && !resizer)
|
|
resizer = new QWidgetResizeHandler(q);
|
|
if (resizer)
|
|
resizer->setEnabled(active);
|
|
#endif
|
|
}
|
|
|
|
bool QDockWidgetPrivate::isAnimating() const
|
|
{
|
|
Q_Q(const QDockWidget);
|
|
|
|
QMainWindowLayout *mainWinLayout = qt_mainwindow_layout_from_dock(q);
|
|
if (mainWinLayout == nullptr)
|
|
return false;
|
|
|
|
return (const void*)mainWinLayout->pluggingWidget == (const void*)q;
|
|
}
|
|
|
|
bool QDockWidgetPrivate::mousePressEvent(QMouseEvent *event)
|
|
{
|
|
#if QT_CONFIG(mainwindow)
|
|
Q_Q(QDockWidget);
|
|
|
|
QDockWidgetLayout *dwLayout
|
|
= qobject_cast<QDockWidgetLayout*>(layout);
|
|
|
|
if (!dwLayout->nativeWindowDeco()) {
|
|
QRect titleArea = dwLayout->titleArea();
|
|
|
|
QDockWidgetGroupWindow *floatingTab = qobject_cast<QDockWidgetGroupWindow*>(parent);
|
|
|
|
if (event->button() != Qt::LeftButton ||
|
|
!titleArea.contains(event->position().toPoint()) ||
|
|
// check if the tool window is movable... do nothing if it
|
|
// is not (but allow moving if the window is floating)
|
|
(!hasFeature(this, QDockWidget::DockWidgetMovable) && !q->isFloating()) ||
|
|
(qobject_cast<QMainWindow*>(parent) == nullptr && !floatingTab) ||
|
|
isAnimating() || state != nullptr) {
|
|
return false;
|
|
}
|
|
|
|
initDrag(event->position().toPoint(), false);
|
|
|
|
if (state)
|
|
state->ctrlDrag = (hasFeature(this, QDockWidget::DockWidgetFloatable) && event->modifiers() & Qt::ControlModifier) ||
|
|
(!hasFeature(this, QDockWidget::DockWidgetMovable) && q->isFloating());
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif // QT_CONFIG(mainwindow)
|
|
return false;
|
|
}
|
|
|
|
bool QDockWidgetPrivate::mouseDoubleClickEvent(QMouseEvent *event)
|
|
{
|
|
QDockWidgetLayout *dwLayout = qobject_cast<QDockWidgetLayout*>(layout);
|
|
|
|
if (!dwLayout->nativeWindowDeco()) {
|
|
QRect titleArea = dwLayout->titleArea();
|
|
|
|
if (event->button() == Qt::LeftButton && titleArea.contains(event->position().toPoint()) &&
|
|
hasFeature(this, QDockWidget::DockWidgetFloatable)) {
|
|
toggleTopLevel();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool QDockWidgetPrivate::isTabbed() const
|
|
{
|
|
Q_Q(const QDockWidget);
|
|
QDockWidget *that = const_cast<QDockWidget *>(q);
|
|
auto *mwLayout = qt_mainwindow_layout_from_dock(that);
|
|
Q_ASSERT(mwLayout);
|
|
return mwLayout->isDockWidgetTabbed(q);
|
|
}
|
|
|
|
bool QDockWidgetPrivate::mouseMoveEvent(QMouseEvent *event)
|
|
{
|
|
bool ret = false;
|
|
#if QT_CONFIG(mainwindow)
|
|
Q_Q(QDockWidget);
|
|
|
|
if (!state)
|
|
return ret;
|
|
|
|
QDockWidgetLayout *dwlayout
|
|
= qobject_cast<QDockWidgetLayout *>(layout);
|
|
QMainWindowLayout *mwlayout = qt_mainwindow_layout_from_dock(q);
|
|
if (!dwlayout->nativeWindowDeco()) {
|
|
if (!state->dragging
|
|
&& mwlayout->pluggingWidget == nullptr
|
|
&& (event->position().toPoint() - state->pressPos).manhattanLength()
|
|
> QApplication::startDragDistance()) {
|
|
|
|
#ifdef Q_OS_MACOS
|
|
if (windowHandle() && !q->isFloating()) {
|
|
// When using native widgets on mac, we have not yet been successful in
|
|
// starting a drag on an NSView that belongs to one window (QMainWindow),
|
|
// but continue the drag on another (QDockWidget). This is what happens if
|
|
// we try to make this widget floating during a drag. So as a fall back
|
|
// solution, we simply make this widget floating instead, when we would
|
|
// otherwise start a drag.
|
|
q->setFloating(true);
|
|
} else
|
|
#endif
|
|
{
|
|
const DragScope scope = isTabbed() ? DragScope::Group : DragScope::Widget;
|
|
startDrag(scope);
|
|
q->grabMouse();
|
|
ret = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (state && state->dragging && !state->nca) {
|
|
QMargins windowMargins = q->window()->windowHandle()->frameMargins();
|
|
QPoint windowMarginOffset = QPoint(windowMargins.left(), windowMargins.top());
|
|
|
|
// TODO maybe use QScreen API (if/when available) to simplify the below code.
|
|
const QScreen *orgWdgScreen = QGuiApplication::screenAt(state->widgetInitialPos);
|
|
const QScreen *screenFrom = QGuiApplication::screenAt(state->globalPressPos);
|
|
const QScreen *screenTo = QGuiApplication::screenAt(event->globalPosition().toPoint());
|
|
const QScreen *wdgScreen = q->screen();
|
|
|
|
QPoint pos;
|
|
if (Q_LIKELY(screenFrom && screenTo && wdgScreen && orgWdgScreen)) {
|
|
const QPoint nativeWdgOrgPos = QHighDpiScaling::mapPositionToNative(
|
|
state->widgetInitialPos, orgWdgScreen->handle());
|
|
const QPoint nativeTo = QHighDpiScaling::mapPositionToNative(
|
|
event->globalPosition().toPoint(), screenTo->handle());
|
|
const QPoint nativeFrom = QHighDpiScaling::mapPositionToNative(state->globalPressPos,
|
|
screenFrom->handle());
|
|
|
|
// Calculate new nativePos based on startPos + mouse delta move.
|
|
const QPoint nativeNewPos = nativeWdgOrgPos + (nativeTo - nativeFrom);
|
|
pos = QHighDpiScaling::mapPositionFromNative(nativeNewPos, wdgScreen->handle())
|
|
- windowMarginOffset;
|
|
} else {
|
|
// Fallback in the unlikely case that source and target screens could not be established
|
|
qCDebug(lcQpaDockWidgets)
|
|
<< "QDockWidget failed to find relevant screen info. screenFrom:" << screenFrom
|
|
<< "screenTo:" << screenTo << " wdgScreen:" << wdgScreen << "orgWdgScreen"
|
|
<< orgWdgScreen;
|
|
pos = event->globalPosition().toPoint() - state->pressPos - windowMarginOffset;
|
|
}
|
|
|
|
// If the newly floating dock widget has got a native title bar,
|
|
// offset the position by the native title bar's height or width
|
|
const int dx = q->geometry().x() - q->x();
|
|
const int dy = q->geometry().y() - q->y();
|
|
pos.rx() += dx;
|
|
pos.ry() += dy;
|
|
|
|
QDockWidgetGroupWindow *floatingTab = qobject_cast<QDockWidgetGroupWindow*>(parent);
|
|
if (floatingTab && !q->isFloating())
|
|
floatingTab->move(pos);
|
|
else
|
|
q->move(pos);
|
|
if (state && !state->ctrlDrag)
|
|
mwlayout->hover(state->widgetItem, event->globalPosition().toPoint());
|
|
|
|
ret = true;
|
|
}
|
|
|
|
#endif // QT_CONFIG(mainwindow)
|
|
return ret;
|
|
}
|
|
|
|
bool QDockWidgetPrivate::mouseReleaseEvent(QMouseEvent *event)
|
|
{
|
|
#if QT_CONFIG(mainwindow)
|
|
// if we are peforming a platform drag ignore the release here and end the drag when the actual
|
|
// drag ends.
|
|
if (QMainWindowLayout::needsPlatformDrag())
|
|
return false;
|
|
|
|
if (event->button() == Qt::LeftButton && state && !state->nca) {
|
|
endDrag(EndDragMode::LocationChange);
|
|
return true; //filter out the event
|
|
}
|
|
|
|
#endif // QT_CONFIG(mainwindow)
|
|
return false;
|
|
}
|
|
|
|
void QDockWidgetPrivate::nonClientAreaMouseEvent(QMouseEvent *event)
|
|
{
|
|
Q_Q(QDockWidget);
|
|
|
|
int fw = q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, nullptr, q);
|
|
|
|
QWidget *tl = q->topLevelWidget();
|
|
QRect geo = tl->geometry();
|
|
QRect titleRect = tl->frameGeometry();
|
|
{
|
|
titleRect.setLeft(geo.left());
|
|
titleRect.setRight(geo.right());
|
|
titleRect.setBottom(geo.top() - 1);
|
|
titleRect.adjust(0, fw, 0, 0);
|
|
}
|
|
|
|
switch (event->type()) {
|
|
case QEvent::NonClientAreaMouseButtonPress:
|
|
if (!titleRect.contains(event->globalPosition().toPoint()))
|
|
break;
|
|
if (state != nullptr)
|
|
break;
|
|
if (qobject_cast<QMainWindow*>(parent) == nullptr && qobject_cast<QDockWidgetGroupWindow*>(parent) == nullptr)
|
|
break;
|
|
if (isAnimating())
|
|
break;
|
|
initDrag(event->position().toPoint(), true);
|
|
if (state == nullptr)
|
|
break;
|
|
state->ctrlDrag = (event->modifiers() & Qt::ControlModifier) ||
|
|
(!hasFeature(this, QDockWidget::DockWidgetMovable) && q->isFloating());
|
|
startDrag(DragScope::Group);
|
|
break;
|
|
case QEvent::NonClientAreaMouseMove:
|
|
if (state == nullptr || !state->dragging)
|
|
break;
|
|
|
|
#if !defined(Q_OS_MAC) && !defined(Q_OS_WASM)
|
|
if (state->nca)
|
|
endDrag(EndDragMode::LocationChange);
|
|
#endif
|
|
break;
|
|
case QEvent::NonClientAreaMouseButtonRelease:
|
|
#if defined(Q_OS_MAC) || defined(Q_OS_WASM)
|
|
if (state)
|
|
endDrag(EndDragMode::LocationChange);
|
|
#endif
|
|
break;
|
|
case QEvent::NonClientAreaMouseButtonDblClick:
|
|
toggleTopLevel();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void QDockWidgetPrivate::recalculatePressPos(QResizeEvent *event)
|
|
{
|
|
qreal ratio = event->oldSize().width() / (1.0 * event->size().width());
|
|
state->pressPos.setX(state->pressPos.x() / ratio);
|
|
}
|
|
|
|
/*! \internal
|
|
Called when the QDockWidget or the QDockWidgetGroupWindow is moved
|
|
*/
|
|
void QDockWidgetPrivate::moveEvent(QMoveEvent *event)
|
|
{
|
|
Q_Q(QDockWidget);
|
|
|
|
if (state == nullptr || !state->dragging || !state->nca)
|
|
return;
|
|
|
|
if (!q->isWindow() && qobject_cast<QDockWidgetGroupWindow*>(parent) == nullptr)
|
|
return;
|
|
|
|
// When the native window frame is being dragged, all we get is these mouse
|
|
// move events.
|
|
|
|
if (state->ctrlDrag)
|
|
return;
|
|
|
|
QMainWindowLayout *layout = qt_mainwindow_layout_from_dock(q);
|
|
Q_ASSERT(layout != nullptr);
|
|
|
|
QPoint globalMousePos = event->pos() + state->pressPos;
|
|
layout->hover(state->widgetItem, globalMousePos);
|
|
}
|
|
|
|
void QDockWidgetPrivate::unplug(const QRect &rect)
|
|
{
|
|
Q_Q(QDockWidget);
|
|
QRect r = rect;
|
|
r.moveTopLeft(q->mapToGlobal(QPoint(0, 0)));
|
|
QDockWidgetLayout *dwLayout = qobject_cast<QDockWidgetLayout*>(layout);
|
|
if (dwLayout->nativeWindowDeco(true))
|
|
r.adjust(0, dwLayout->titleHeight(), 0, 0);
|
|
setWindowState(true, true, r);
|
|
}
|
|
|
|
void QDockWidgetPrivate::plug(const QRect &rect)
|
|
{
|
|
setWindowState(false, false, rect);
|
|
}
|
|
|
|
void QDockWidgetPrivate::setWindowState(bool floating, bool unplug, const QRect &rect)
|
|
{
|
|
Q_Q(QDockWidget);
|
|
|
|
if (!floating && parent) {
|
|
QMainWindowLayout *mwlayout = qt_mainwindow_layout_from_dock(q);
|
|
if (mwlayout && mwlayout->dockWidgetArea(q) == Qt::NoDockWidgetArea
|
|
&& !qobject_cast<QDockWidgetGroupWindow *>(parent))
|
|
return; // this dockwidget can't be redocked
|
|
}
|
|
|
|
const bool wasFloating = q->isFloating();
|
|
if (wasFloating) // Prevent repetitive unplugging from nested invocations (QTBUG-42818)
|
|
unplug = false;
|
|
const bool hidden = q->isHidden();
|
|
|
|
if (q->isVisible())
|
|
q->hide();
|
|
|
|
Qt::WindowFlags flags = floating ? Qt::Tool : Qt::Widget;
|
|
|
|
QDockWidgetLayout *dwLayout = qobject_cast<QDockWidgetLayout*>(layout);
|
|
const bool nativeDeco = dwLayout->nativeWindowDeco(floating);
|
|
|
|
if (nativeDeco) {
|
|
flags |= Qt::CustomizeWindowHint | Qt::WindowTitleHint;
|
|
if (hasFeature(this, QDockWidget::DockWidgetClosable))
|
|
flags |= Qt::WindowCloseButtonHint;
|
|
} else {
|
|
flags |= Qt::FramelessWindowHint;
|
|
}
|
|
|
|
// If we are performing a platform drag the flag is not needed and we want to avoid recreating
|
|
// the platform window when it would be removed later
|
|
if (unplug && !QMainWindowLayout::needsPlatformDrag())
|
|
flags |= Qt::X11BypassWindowManagerHint;
|
|
|
|
q->setWindowFlags(flags);
|
|
|
|
|
|
if (!rect.isNull())
|
|
q->setGeometry(rect);
|
|
|
|
updateButtons();
|
|
|
|
if (!hidden)
|
|
q->show();
|
|
|
|
if (floating != wasFloating) {
|
|
emit q->topLevelChanged(floating);
|
|
if (!floating && parent) {
|
|
QMainWindowLayout *mwlayout = qt_mainwindow_layout_from_dock(q);
|
|
if (mwlayout)
|
|
emit q->dockLocationChanged(mwlayout->dockWidgetArea(q));
|
|
} else {
|
|
emit q->dockLocationChanged(Qt::NoDockWidgetArea);
|
|
}
|
|
}
|
|
|
|
setResizerActive(!unplug && floating && !nativeDeco);
|
|
}
|
|
|
|
/*!
|
|
\class QDockWidget
|
|
|
|
\brief The QDockWidget class provides a widget that can be docked
|
|
inside a QMainWindow or floated as a top-level window on the
|
|
desktop.
|
|
|
|
\ingroup mainwindow-classes
|
|
\inmodule QtWidgets
|
|
|
|
QDockWidget provides the concept of dock widgets, also know as
|
|
tool palettes or utility windows. Dock windows are secondary
|
|
windows placed in the \e {dock widget area} around the
|
|
\l{QMainWindow::centralWidget()}{central widget} in a
|
|
QMainWindow.
|
|
|
|
\image mainwindow-docks.png
|
|
|
|
Dock windows can be moved inside their current area, moved into
|
|
new areas and floated (e.g., undocked) by the end-user. The
|
|
QDockWidget API allows the programmer to restrict the dock widgets
|
|
ability to move, float and close, as well as the areas in which
|
|
they can be placed.
|
|
|
|
\section1 Appearance
|
|
|
|
A QDockWidget consists of a title bar and the content area. The
|
|
title bar displays the dock widgets
|
|
\l{QWidget::windowTitle()}{window title},
|
|
a \e float button and a \e close button.
|
|
Depending on the state of the QDockWidget, the \e float and \e
|
|
close buttons may be either disabled or not shown at all.
|
|
|
|
The visual appearance of the title bar and buttons is dependent
|
|
on the \l{QStyle}{style} in use.
|
|
|
|
A QDockWidget acts as a wrapper for its child widget, set with setWidget().
|
|
Custom size hints, minimum and maximum sizes and size policies should be
|
|
implemented in the child widget. QDockWidget will respect them, adjusting
|
|
its own constraints to include the frame and title. Size constraints
|
|
should not be set on the QDockWidget itself, because they change depending
|
|
on whether it is docked; a docked QDockWidget has no frame and a smaller title
|
|
bar.
|
|
|
|
\note On macOS, if the QDockWidget has a native window handle (for example,
|
|
if winId() is called on it or the child widget), then due to a limitation it will not be
|
|
possible to drag the dock widget when undocking. Starting the drag will undock
|
|
the dock widget, but a second drag will be needed to move the dock widget itself.
|
|
|
|
\sa QMainWindow
|
|
*/
|
|
|
|
/*!
|
|
\enum QDockWidget::DockWidgetFeature
|
|
|
|
\value DockWidgetClosable The dock widget can be closed. On some systems the dock
|
|
widget always has a close button when it's floating
|
|
(for example on MacOS 10.5).
|
|
\value DockWidgetMovable The dock widget can be moved between docks
|
|
by the user.
|
|
\value DockWidgetFloatable The dock widget can be detached from the
|
|
main window, and floated as an independent
|
|
window.
|
|
\value DockWidgetVerticalTitleBar The dock widget displays a vertical title
|
|
bar on its left side. This can be used to
|
|
increase the amount of vertical space in
|
|
a QMainWindow.
|
|
\value NoDockWidgetFeatures The dock widget cannot be closed, moved,
|
|
or floated.
|
|
|
|
\omitvalue DockWidgetFeatureMask
|
|
\omitvalue Reserved
|
|
*/
|
|
|
|
/*!
|
|
\property QDockWidget::windowTitle
|
|
\brief the dock widget title (caption)
|
|
|
|
By default, this property contains an empty string.
|
|
*/
|
|
|
|
/*!
|
|
Constructs a QDockWidget with parent \a parent and window flags \a
|
|
flags. The dock widget will be placed in the left dock widget
|
|
area.
|
|
*/
|
|
QDockWidget::QDockWidget(QWidget *parent, Qt::WindowFlags flags)
|
|
: QWidget(*new QDockWidgetPrivate, parent, flags)
|
|
{
|
|
Q_D(QDockWidget);
|
|
d->init();
|
|
}
|
|
|
|
/*!
|
|
Constructs a QDockWidget with parent \a parent and window flags \a
|
|
flags. The dock widget will be placed in the left dock widget
|
|
area.
|
|
|
|
The window title is set to \a title. This title is used when the
|
|
QDockWidget is docked and undocked. It is also used in the context
|
|
menu provided by QMainWindow.
|
|
|
|
\sa setWindowTitle()
|
|
*/
|
|
QDockWidget::QDockWidget(const QString &title, QWidget *parent, Qt::WindowFlags flags)
|
|
: QDockWidget(parent, flags)
|
|
{
|
|
setWindowTitle(title);
|
|
}
|
|
|
|
/*!
|
|
Destroys the dock widget.
|
|
*/
|
|
QDockWidget::~QDockWidget()
|
|
{ }
|
|
|
|
/*!
|
|
Returns the widget for the dock widget. This function returns zero
|
|
if the widget has not been set.
|
|
|
|
\sa setWidget()
|
|
*/
|
|
QWidget *QDockWidget::widget() const
|
|
{
|
|
QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(this->layout());
|
|
return layout->widgetForRole(QDockWidgetLayout::Content);
|
|
}
|
|
|
|
/*!
|
|
Sets the widget for the dock widget to \a widget.
|
|
|
|
If the dock widget is visible when \a widget is added, you must
|
|
\l{QWidget::}{show()} it explicitly.
|
|
|
|
Note that you must add the layout of the \a widget before you call
|
|
this function; if not, the \a widget will not be visible.
|
|
|
|
\sa widget()
|
|
*/
|
|
void QDockWidget::setWidget(QWidget *widget)
|
|
{
|
|
QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(this->layout());
|
|
layout->setWidgetForRole(QDockWidgetLayout::Content, widget);
|
|
}
|
|
|
|
/*!
|
|
\property QDockWidget::features
|
|
\brief whether the dock widget is movable, closable, and floatable
|
|
|
|
By default, this property is set to a combination of DockWidgetClosable,
|
|
DockWidgetMovable and DockWidgetFloatable.
|
|
|
|
\sa DockWidgetFeature
|
|
*/
|
|
|
|
void QDockWidget::setFeatures(QDockWidget::DockWidgetFeatures features)
|
|
{
|
|
Q_D(QDockWidget);
|
|
features &= DockWidgetFeatureMask;
|
|
if (d->features == features)
|
|
return;
|
|
const bool closableChanged = (d->features ^ features) & DockWidgetClosable;
|
|
d->features = features;
|
|
QDockWidgetLayout *layout
|
|
= qobject_cast<QDockWidgetLayout*>(this->layout());
|
|
layout->setVerticalTitleBar(features & DockWidgetVerticalTitleBar);
|
|
d->updateButtons();
|
|
d->toggleViewAction->setEnabled((d->features & DockWidgetClosable) == DockWidgetClosable);
|
|
emit featuresChanged(d->features);
|
|
update();
|
|
if (closableChanged && layout->nativeWindowDeco()) {
|
|
QDockWidgetGroupWindow *floatingTab = qobject_cast<QDockWidgetGroupWindow *>(parent());
|
|
if (floatingTab && !isFloating())
|
|
floatingTab->adjustFlags();
|
|
else
|
|
d->setWindowState(true /*floating*/, true /*unplug*/); //this ensures the native decoration is drawn
|
|
}
|
|
}
|
|
|
|
QDockWidget::DockWidgetFeatures QDockWidget::features() const
|
|
{
|
|
Q_D(const QDockWidget);
|
|
return d->features;
|
|
}
|
|
|
|
/*!
|
|
\property QDockWidget::floating
|
|
\brief whether the dock widget is floating
|
|
|
|
A floating dock widget is presented to the user as a single, independent
|
|
window "on top" of its parent QMainWindow, instead of being docked
|
|
either in the QMainWindow, or in a group of tabbed dock widgets.
|
|
|
|
Floating dock widgets can be individually positioned and resized, both
|
|
programmatically or by mouse interaction.
|
|
|
|
By default, this property is \c true.
|
|
|
|
When this property changes, the \c {topLevelChanged()} signal is emitted.
|
|
|
|
\sa isWindow(), topLevelChanged()
|
|
*/
|
|
void QDockWidget::setFloating(bool floating)
|
|
{
|
|
Q_D(QDockWidget);
|
|
|
|
// the initial click of a double-click may have started a drag...
|
|
if (d->state != nullptr)
|
|
d->endDrag(QDockWidgetPrivate::EndDragMode::Abort);
|
|
|
|
QRect r = d->undockedGeometry;
|
|
// Keep position when undocking for the first time.
|
|
if (floating && isVisible() && !r.isValid())
|
|
r = QRect(mapToGlobal(QPoint(0, 0)), size());
|
|
|
|
d->setWindowState(floating, false, floating ? r : QRect());
|
|
|
|
if (floating && r.isNull()) {
|
|
if (x() < 0 || y() < 0) //may happen if we have been hidden
|
|
move(QPoint());
|
|
setAttribute(Qt::WA_Moved, false); //we want it at the default position
|
|
}
|
|
}
|
|
|
|
/*!
|
|
\property QDockWidget::allowedAreas
|
|
\brief areas where the dock widget may be placed
|
|
|
|
The default is Qt::AllDockWidgetAreas.
|
|
|
|
\sa Qt::DockWidgetArea
|
|
*/
|
|
|
|
void QDockWidget::setAllowedAreas(Qt::DockWidgetAreas areas)
|
|
{
|
|
Q_D(QDockWidget);
|
|
areas &= Qt::DockWidgetArea_Mask;
|
|
if (areas == d->allowedAreas)
|
|
return;
|
|
d->allowedAreas = areas;
|
|
emit allowedAreasChanged(d->allowedAreas);
|
|
}
|
|
|
|
Qt::DockWidgetAreas QDockWidget::allowedAreas() const
|
|
{
|
|
Q_D(const QDockWidget);
|
|
return d->allowedAreas;
|
|
}
|
|
|
|
/*!
|
|
\fn bool QDockWidget::isAreaAllowed(Qt::DockWidgetArea area) const
|
|
|
|
Returns \c true if this dock widget can be placed in the given \a area;
|
|
otherwise returns \c false.
|
|
*/
|
|
|
|
/*! \reimp */
|
|
void QDockWidget::changeEvent(QEvent *event)
|
|
{
|
|
Q_D(QDockWidget);
|
|
QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(this->layout());
|
|
|
|
switch (event->type()) {
|
|
case QEvent::WindowTitleChange:
|
|
if (isFloating() && windowHandle() && d->topData() && windowHandle()->isVisible()) {
|
|
// From QWidget::setWindowTitle(): Propagate window title without signal emission
|
|
d->topData()->caption = windowHandle()->title();
|
|
d->setWindowTitle_helper(windowHandle()->title());
|
|
}
|
|
Q_FALLTHROUGH();
|
|
case QEvent::ModifiedChange:
|
|
update(layout->titleArea());
|
|
#ifndef QT_NO_ACTION
|
|
d->fixedWindowTitle = qt_setWindowTitle_helperHelper(windowTitle(), this);
|
|
d->toggleViewAction->setText(d->fixedWindowTitle);
|
|
#endif
|
|
#if QT_CONFIG(tabbar)
|
|
{
|
|
if (QMainWindowLayout *winLayout = qt_mainwindow_layout_from_dock(this)) {
|
|
if (QDockAreaLayoutInfo *info = winLayout->layoutState.dockAreaLayout.info(this))
|
|
info->updateTabBar();
|
|
}
|
|
}
|
|
#endif // QT_CONFIG(tabbar)
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
QWidget::changeEvent(event);
|
|
}
|
|
|
|
/*! \reimp */
|
|
void QDockWidget::closeEvent(QCloseEvent *event)
|
|
{
|
|
Q_D(QDockWidget);
|
|
if (d->state)
|
|
d->endDrag(QDockWidgetPrivate::EndDragMode::Abort);
|
|
|
|
// For non-closable widgets, don't allow closing, except when the mainwindow
|
|
// is hidden, as otherwise an application wouldn't be able to be shut down.
|
|
const QMainWindow *win = qobject_cast<QMainWindow*>(parentWidget());
|
|
const bool canClose = (d->features & DockWidgetClosable)
|
|
|| (!win || !win->isVisible());
|
|
event->setAccepted(canClose);
|
|
}
|
|
|
|
/*! \reimp */
|
|
void QDockWidget::paintEvent(QPaintEvent *event)
|
|
{
|
|
Q_UNUSED(event);
|
|
Q_D(QDockWidget);
|
|
|
|
QDockWidgetLayout *layout
|
|
= qobject_cast<QDockWidgetLayout*>(this->layout());
|
|
bool customTitleBar = layout->widgetForRole(QDockWidgetLayout::TitleBar) != nullptr;
|
|
bool nativeDeco = layout->nativeWindowDeco();
|
|
|
|
if (!nativeDeco && !customTitleBar) {
|
|
QStylePainter p(this);
|
|
// ### Add PixelMetric to change spacers, so style may show border
|
|
// when not floating.
|
|
if (isFloating()) {
|
|
QStyleOptionFrame framOpt;
|
|
framOpt.initFrom(this);
|
|
p.drawPrimitive(QStyle::PE_FrameDockWidget, framOpt);
|
|
}
|
|
|
|
// Title must be painted after the frame, since the areas overlap, and
|
|
// the title may wish to extend out to all sides (eg. Vista style)
|
|
QStyleOptionDockWidget titleOpt;
|
|
initStyleOption(&titleOpt);
|
|
if (font() == QApplication::font("QDockWidget")) {
|
|
titleOpt.fontMetrics = QFontMetrics(d->font);
|
|
p.setFont(d->font);
|
|
}
|
|
|
|
p.drawControl(QStyle::CE_DockWidgetTitle, titleOpt);
|
|
}
|
|
}
|
|
|
|
/*! \reimp */
|
|
bool QDockWidget::event(QEvent *event)
|
|
{
|
|
Q_D(QDockWidget);
|
|
|
|
QMainWindow *win = qobject_cast<QMainWindow*>(parentWidget());
|
|
QMainWindowLayout *layout = qt_mainwindow_layout_from_dock(this);
|
|
|
|
switch (event->type()) {
|
|
#ifndef QT_NO_ACTION
|
|
case QEvent::Hide:
|
|
if (layout != nullptr)
|
|
layout->keepSize(this);
|
|
d->toggleViewAction->setChecked(false);
|
|
emit visibilityChanged(false);
|
|
break;
|
|
case QEvent::Show: {
|
|
d->toggleViewAction->setChecked(true);
|
|
QPoint parentTopLeft(0, 0);
|
|
if (isWindow()) {
|
|
const QScreen *screen = d->associatedScreen();
|
|
parentTopLeft = screen
|
|
? screen->availableVirtualGeometry().topLeft()
|
|
: QGuiApplication::primaryScreen()->availableVirtualGeometry().topLeft();
|
|
}
|
|
emit visibilityChanged(geometry().right() >= parentTopLeft.x() && geometry().bottom() >= parentTopLeft.y());
|
|
}
|
|
break;
|
|
#endif
|
|
case QEvent::ApplicationLayoutDirectionChange:
|
|
case QEvent::LayoutDirectionChange:
|
|
case QEvent::StyleChange:
|
|
case QEvent::ParentChange:
|
|
d->updateButtons();
|
|
break;
|
|
case QEvent::ZOrderChange: {
|
|
bool onTop = false;
|
|
if (win != nullptr) {
|
|
const QObjectList &siblings = win->children();
|
|
onTop = siblings.size() > 0 && siblings.last() == (QObject*)this;
|
|
}
|
|
#if QT_CONFIG(tabbar)
|
|
if (!isFloating() && layout != nullptr && onTop)
|
|
layout->raise(this);
|
|
#endif
|
|
break;
|
|
}
|
|
case QEvent::WindowActivate:
|
|
case QEvent::WindowDeactivate:
|
|
update(qobject_cast<QDockWidgetLayout *>(this->layout())->titleArea());
|
|
break;
|
|
case QEvent::ContextMenu:
|
|
if (d->state) {
|
|
event->accept();
|
|
return true;
|
|
}
|
|
break;
|
|
// return true after calling the handler since we don't want
|
|
// them to be passed onto the default handlers
|
|
case QEvent::MouseButtonPress:
|
|
if (d->mousePressEvent(static_cast<QMouseEvent *>(event)))
|
|
return true;
|
|
break;
|
|
case QEvent::MouseButtonDblClick:
|
|
if (d->mouseDoubleClickEvent(static_cast<QMouseEvent *>(event)))
|
|
return true;
|
|
break;
|
|
case QEvent::MouseMove:
|
|
if (d->mouseMoveEvent(static_cast<QMouseEvent *>(event)))
|
|
return true;
|
|
break;
|
|
case QEvent::MouseButtonRelease:
|
|
if (d->mouseReleaseEvent(static_cast<QMouseEvent *>(event)))
|
|
return true;
|
|
break;
|
|
case QEvent::NonClientAreaMouseMove:
|
|
case QEvent::NonClientAreaMouseButtonPress:
|
|
case QEvent::NonClientAreaMouseButtonRelease:
|
|
case QEvent::NonClientAreaMouseButtonDblClick:
|
|
d->nonClientAreaMouseEvent(static_cast<QMouseEvent*>(event));
|
|
return true;
|
|
case QEvent::Move:
|
|
d->moveEvent(static_cast<QMoveEvent*>(event));
|
|
break;
|
|
case QEvent::Resize:
|
|
// if the mainwindow is plugging us, we don't want to update undocked geometry
|
|
if (isFloating() && layout != nullptr && layout->pluggingWidget != this)
|
|
d->undockedGeometry = geometry();
|
|
|
|
// Usually the window won't get resized while it's being moved, but it can happen,
|
|
// for example on Windows when moving to a screen with bigger scale factor
|
|
// If that happens we should update state->pressPos, otherwise it will be outside
|
|
// the window when the window shrinks.
|
|
if (d->state && d->state->dragging)
|
|
d->recalculatePressPos(static_cast<QResizeEvent*>(event));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return QWidget::event(event);
|
|
}
|
|
|
|
#ifndef QT_NO_ACTION
|
|
/*!
|
|
Returns a checkable action that can be added to menus and toolbars so that
|
|
the user can show or close this dock widget.
|
|
|
|
The action's text is set to the dock widget's window title.
|
|
|
|
\note The action can not be used to programmatically show or hide the dock
|
|
widget. Use the \l visible property for that.
|
|
|
|
\sa QAction::text, QWidget::windowTitle
|
|
*/
|
|
QAction * QDockWidget::toggleViewAction() const
|
|
{
|
|
Q_D(const QDockWidget);
|
|
return d->toggleViewAction;
|
|
}
|
|
#endif // QT_NO_ACTION
|
|
|
|
/*!
|
|
\fn void QDockWidget::featuresChanged(QDockWidget::DockWidgetFeatures features)
|
|
|
|
This signal is emitted when the \l features property changes. The
|
|
\a features parameter gives the new value of the property.
|
|
*/
|
|
|
|
/*!
|
|
\fn void QDockWidget::topLevelChanged(bool topLevel)
|
|
|
|
This signal is emitted when the \l floating property changes.
|
|
The \a topLevel parameter is true if the dock widget is now floating;
|
|
otherwise it is false.
|
|
|
|
\sa isWindow()
|
|
*/
|
|
|
|
/*!
|
|
\fn void QDockWidget::allowedAreasChanged(Qt::DockWidgetAreas allowedAreas)
|
|
|
|
This signal is emitted when the \l allowedAreas property changes. The
|
|
\a allowedAreas parameter gives the new value of the property.
|
|
*/
|
|
|
|
/*!
|
|
\fn void QDockWidget::visibilityChanged(bool visible)
|
|
\since 4.3
|
|
|
|
This signal is emitted when the dock widget becomes \a visible (or
|
|
invisible). This happens when the widget is hidden or shown, as
|
|
well as when it is docked in a tabbed dock area and its tab
|
|
becomes selected or unselected.
|
|
|
|
\note The signal can differ from QWidget::isVisible(). This can be the case, if
|
|
a dock widget is minimized or tabified and associated to a non-selected or
|
|
inactive tab.
|
|
*/
|
|
|
|
/*!
|
|
\fn void QDockWidget::dockLocationChanged(Qt::DockWidgetArea area)
|
|
\since 4.3
|
|
|
|
This signal is emitted when the dock widget is moved to another
|
|
dock \a area, or is moved to a different location in its current
|
|
dock area. This happens when the dock widget is moved
|
|
programmatically or is dragged to a new location by the user.
|
|
*/
|
|
|
|
/*!
|
|
\since 4.3
|
|
|
|
Sets an arbitrary \a widget as the dock widget's title bar. If \a widget
|
|
is \nullptr, any custom title bar widget previously set on the dock widget
|
|
is removed, but not deleted, and the default title bar will be used
|
|
instead.
|
|
|
|
If a title bar widget is set, QDockWidget will not use native window
|
|
decorations when it is floated.
|
|
|
|
Here are some tips for implementing custom title bars:
|
|
|
|
\list
|
|
\li Mouse events that are not explicitly handled by the title bar widget
|
|
must be ignored by calling QMouseEvent::ignore(). These events then
|
|
propagate to the QDockWidget parent, which handles them in the usual
|
|
manner, moving when the title bar is dragged, docking and undocking
|
|
when it is double-clicked, etc.
|
|
|
|
\li When DockWidgetVerticalTitleBar is set on QDockWidget, the title
|
|
bar widget is repositioned accordingly. In resizeEvent(), the title
|
|
bar should check what orientation it should assume:
|
|
\snippet code/src_gui_widgets_qdockwidget.cpp 0
|
|
|
|
\li The title bar widget must have a valid QWidget::sizeHint() and
|
|
QWidget::minimumSizeHint(). These functions should take into account
|
|
the current orientation of the title bar.
|
|
|
|
\li It is not possible to remove a title bar from a dock widget. However,
|
|
a similar effect can be achieved by setting a default constructed
|
|
QWidget as the title bar widget.
|
|
\endlist
|
|
|
|
Using qobject_cast() as shown above, the title bar widget has full access
|
|
to its parent QDockWidget. Hence it can perform such operations as docking
|
|
and hiding in response to user actions.
|
|
|
|
\sa titleBarWidget(), DockWidgetVerticalTitleBar
|
|
*/
|
|
|
|
void QDockWidget::setTitleBarWidget(QWidget *widget)
|
|
{
|
|
Q_D(QDockWidget);
|
|
QDockWidgetLayout *layout
|
|
= qobject_cast<QDockWidgetLayout*>(this->layout());
|
|
layout->setWidgetForRole(QDockWidgetLayout::TitleBar, widget);
|
|
d->updateButtons();
|
|
if (isWindow()) {
|
|
//this ensures the native decoration is drawn
|
|
d->setWindowState(true /*floating*/, true /*unplug*/);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
\since 4.3
|
|
Returns the custom title bar widget set on the QDockWidget, or
|
|
\nullptr if no custom title bar has been set.
|
|
|
|
\sa setTitleBarWidget()
|
|
*/
|
|
|
|
QWidget *QDockWidget::titleBarWidget() const
|
|
{
|
|
QDockWidgetLayout *layout
|
|
= qobject_cast<QDockWidgetLayout*>(this->layout());
|
|
return layout->widgetForRole(QDockWidgetLayout::TitleBar);
|
|
}
|
|
|
|
#ifndef QT_NO_DEBUG_STREAM
|
|
QDebug operator<<(QDebug dbg, const QDockWidget *dockWidget)
|
|
{
|
|
QDebugStateSaver saver(dbg);
|
|
dbg.nospace();
|
|
|
|
if (!dockWidget) {
|
|
dbg << "QDockWidget(0x0)";
|
|
return dbg;
|
|
}
|
|
|
|
dbg << "QDockWidget(" << static_cast<const void *>(dockWidget);
|
|
dbg << "->(ObjectName=" << dockWidget->objectName();
|
|
dbg << "; floating=" << dockWidget->isFloating();
|
|
dbg << "; features=" << dockWidget->features();
|
|
dbg << ";))";
|
|
return dbg;
|
|
}
|
|
#endif // QT_NO_DEBUG_STREAM
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
#include "qdockwidget.moc"
|
|
#include "moc_qdockwidget.cpp"
|
|
#include "moc_qdockwidget_p.cpp"
|