1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qdockwidget.h"
5
6#include <qaction.h>
7#include <qapplication.h>
8#include <qdrawutil.h>
9#include <qevent.h>
10#include <qfontmetrics.h>
11#include <qproxystyle.h>
12#include <qwindow.h>
13#include <qscreen.h>
14#include <qmainwindow.h>
15#include <qstylepainter.h>
16#include <qtoolbutton.h>
17#include <qdebug.h>
18
19#include <private/qwidgetresizehandler_p.h>
20#include <qpa/qplatformtheme.h>
21
22#include <private/qhighdpiscaling_p.h>
23#include "qdockwidget_p.h"
24#include "qmainwindowlayout_p.h"
25
26QT_BEGIN_NAMESPACE
27
28using namespace Qt::StringLiterals;
29
30extern QString qt_setWindowTitle_helperHelper(const QString&, const QWidget*); // qwidget.cpp
31
32// qmainwindow.cpp
33extern QMainWindowLayout *qt_mainwindow_layout(const QMainWindow *window);
34
35static const QMainWindow *mainwindow_from_dock(const QDockWidget *dock)
36{
37 for (const QWidget *p = dock->parentWidget(); p; p = p->parentWidget()) {
38 if (const QMainWindow *window = qobject_cast<const QMainWindow*>(object: p))
39 return window;
40 }
41 return nullptr;
42}
43
44static inline QMainWindowLayout *qt_mainwindow_layout_from_dock(const QDockWidget *dock)
45{
46 auto mainWindow = mainwindow_from_dock(dock);
47 return mainWindow ? qt_mainwindow_layout(window: mainWindow) : nullptr;
48}
49
50static inline bool hasFeature(const QDockWidgetPrivate *priv, QDockWidget::DockWidgetFeature feature)
51{ return (priv->features & feature) == feature; }
52
53static inline bool hasFeature(const QDockWidget *dockwidget, QDockWidget::DockWidgetFeature feature)
54{ return (dockwidget->features() & feature) == feature; }
55
56
57/*
58 A Dock Window:
59
60 [+] is the float button
61 [X] is the close button
62
63 +-------------------------------+
64 | Dock Window Title [+][X]|
65 +-------------------------------+
66 | |
67 | place to put the single |
68 | QDockWidget child (this space |
69 | does not yet have a name) |
70 | |
71 | |
72 | |
73 | |
74 | |
75 | |
76 | |
77 | |
78 | |
79 +-------------------------------+
80
81*/
82
83/******************************************************************************
84** QDockWidgetTitleButton
85*/
86
87class QDockWidgetTitleButton : public QAbstractButton
88{
89 Q_OBJECT
90
91public:
92 QDockWidgetTitleButton(QDockWidget *dockWidget);
93
94 QSize sizeHint() const override;
95 QSize minimumSizeHint() const override
96 { return sizeHint(); }
97
98 void enterEvent(QEnterEvent *event) override;
99 void leaveEvent(QEvent *event) override;
100 void paintEvent(QPaintEvent *event) override;
101
102protected:
103 bool event(QEvent *event) override;
104
105private:
106 QSize dockButtonIconSize() const;
107
108 mutable int m_iconSize = -1;
109};
110
111QDockWidgetTitleButton::QDockWidgetTitleButton(QDockWidget *dockWidget)
112 : QAbstractButton(dockWidget)
113{
114 setFocusPolicy(Qt::NoFocus);
115}
116
117bool QDockWidgetTitleButton::event(QEvent *event)
118{
119 switch (event->type()) {
120 case QEvent::StyleChange:
121 case QEvent::DevicePixelRatioChange:
122 m_iconSize = -1;
123 break;
124 default:
125 break;
126 }
127 return QAbstractButton::event(e: event);
128}
129
130QSize QDockWidgetTitleButton::dockButtonIconSize() const
131{
132 if (m_iconSize < 0) {
133 m_iconSize = style()->pixelMetric(metric: QStyle::PM_SmallIconSize, option: nullptr, widget: this);
134 if (style()->styleHint(stylehint: QStyle::SH_DockWidget_ButtonsHaveFrame, opt: nullptr, widget: this))
135 m_iconSize = (m_iconSize * 5) / 8; // 16 -> 10
136 }
137 return QSize(m_iconSize, m_iconSize);
138}
139
140QSize QDockWidgetTitleButton::sizeHint() const
141{
142 ensurePolished();
143
144 int size = 2*style()->pixelMetric(metric: QStyle::PM_DockWidgetTitleBarButtonMargin, option: nullptr, widget: this);
145 if (!icon().isNull()) {
146 const QSize sz = icon().actualSize(size: dockButtonIconSize());
147 size += qMax(a: sz.width(), b: sz.height());
148 }
149
150 return QSize(size, size);
151}
152
153void QDockWidgetTitleButton::enterEvent(QEnterEvent *event)
154{
155 if (isEnabled()) update();
156 QAbstractButton::enterEvent(event);
157}
158
159void QDockWidgetTitleButton::leaveEvent(QEvent *event)
160{
161 if (isEnabled()) update();
162 QAbstractButton::leaveEvent(event);
163}
164
165void QDockWidgetTitleButton::paintEvent(QPaintEvent *)
166{
167 QStylePainter p(this);
168
169 QStyleOptionToolButton opt;
170 opt.initFrom(w: this);
171 opt.state |= QStyle::State_AutoRaise;
172
173 if (style()->styleHint(stylehint: QStyle::SH_DockWidget_ButtonsHaveFrame, opt: nullptr, widget: this)) {
174 if (isEnabled() && underMouse() && !isChecked() && !isDown())
175 opt.state |= QStyle::State_Raised;
176 if (isChecked())
177 opt.state |= QStyle::State_On;
178 if (isDown())
179 opt.state |= QStyle::State_Sunken;
180 p.drawPrimitive(pe: QStyle::PE_PanelButtonTool, opt);
181 } else if (isDown() || isChecked()) {
182 // no frame, but the icon might have explicit pixmaps for QIcon::On
183 opt.state |= QStyle::State_On | QStyle::State_Sunken;
184 }
185
186 opt.icon = icon();
187 opt.subControls = { };
188 opt.activeSubControls = { };
189 opt.features = QStyleOptionToolButton::None;
190 opt.arrowType = Qt::NoArrow;
191 opt.iconSize = dockButtonIconSize();
192 p.drawComplexControl(cc: QStyle::CC_ToolButton, opt);
193}
194
195/******************************************************************************
196** QDockWidgetLayout
197*/
198
199QDockWidgetLayout::QDockWidgetLayout(QWidget *parent)
200 : QLayout(parent), verticalTitleBar(false), item_list(RoleCount, 0)
201{
202}
203
204QDockWidgetLayout::~QDockWidgetLayout()
205{
206 qDeleteAll(c: item_list);
207}
208
209/*! \internal
210 Returns true if the dock widget managed by this layout should have a native
211 window decoration or if Qt needs to draw it.
212 */
213bool QDockWidgetLayout::nativeWindowDeco() const
214{
215 bool floating = parentWidget()->isWindow();
216#if QT_CONFIG(tabbar)
217 if (auto groupWindow =
218 qobject_cast<const QDockWidgetGroupWindow *>(object: parentWidget()->parentWidget()))
219 floating = floating || groupWindow->tabLayoutInfo();
220#endif
221 return nativeWindowDeco(floating);
222}
223
224/*! \internal
225 Returns true if the window manager can draw natively the windows decoration
226 of a dock widget
227 */
228bool QDockWidgetLayout::wmSupportsNativeWindowDeco()
229{
230#if defined(Q_OS_ANDROID)
231 return false;
232#else
233 static const bool xcb = !QGuiApplication::platformName().compare(other: "xcb"_L1, cs: Qt::CaseInsensitive);
234 static const bool wayland =
235 QGuiApplication::platformName().startsWith(s: "wayland"_L1, cs: Qt::CaseInsensitive);
236 return !(xcb || wayland);
237#endif
238}
239
240/*! \internal
241 Returns true if the dock widget managed by this layout should have a native
242 window decoration or if Qt needs to draw it. The \a floating parameter
243 overrides the floating current state of the dock widget.
244 */
245bool QDockWidgetLayout::nativeWindowDeco(bool floating) const
246{
247 return wmSupportsNativeWindowDeco() && floating && item_list.at(i: QDockWidgetLayout::TitleBar) == nullptr;
248}
249
250
251void QDockWidgetLayout::addItem(QLayoutItem*)
252{
253 qWarning(msg: "QDockWidgetLayout::addItem(): please use QDockWidgetLayout::setWidget()");
254 return;
255}
256
257QLayoutItem *QDockWidgetLayout::itemAt(int index) const
258{
259 int cnt = 0;
260 for (int i = 0; i < item_list.size(); ++i) {
261 QLayoutItem *item = item_list.at(i);
262 if (item == nullptr)
263 continue;
264 if (index == cnt++)
265 return item;
266 }
267 return nullptr;
268}
269
270QLayoutItem *QDockWidgetLayout::takeAt(int index)
271{
272 int j = 0;
273 for (int i = 0; i < item_list.size(); ++i) {
274 QLayoutItem *item = item_list.at(i);
275 if (item == nullptr)
276 continue;
277 if (index == j) {
278 item_list[i] = 0;
279 invalidate();
280 return item;
281 }
282 ++j;
283 }
284 return nullptr;
285}
286
287int QDockWidgetLayout::count() const
288{
289 int result = 0;
290 for (int i = 0; i < item_list.size(); ++i) {
291 if (item_list.at(i))
292 ++result;
293 }
294 return result;
295}
296
297QSize QDockWidgetLayout::sizeFromContent(const QSize &content, bool floating) const
298{
299 QSize result = content;
300
301 if (verticalTitleBar) {
302 result.setHeight(qMax(a: result.height(), b: minimumTitleWidth()));
303 result.setWidth(qMax(a: content.width(), b: 0));
304 } else {
305 result.setHeight(qMax(a: result.height(), b: 0));
306 result.setWidth(qMax(a: content.width(), b: minimumTitleWidth()));
307 }
308
309 QDockWidget *w = qobject_cast<QDockWidget*>(object: parentWidget());
310 const bool nativeDeco = nativeWindowDeco(floating);
311
312 int fw = floating && !nativeDeco
313 ? w->style()->pixelMetric(metric: QStyle::PM_DockWidgetFrameWidth, option: nullptr, widget: w)
314 : 0;
315
316 const int th = titleHeight();
317 if (!nativeDeco) {
318 if (verticalTitleBar)
319 result += QSize(th + 2*fw, 2*fw);
320 else
321 result += QSize(2*fw, th + 2*fw);
322 }
323
324 result.setHeight(qMin(a: result.height(), b: (int) QWIDGETSIZE_MAX));
325 result.setWidth(qMin(a: result.width(), b: (int) QWIDGETSIZE_MAX));
326
327 if (content.width() < 0)
328 result.setWidth(-1);
329 if (content.height() < 0)
330 result.setHeight(-1);
331
332 const QMargins margins = w->contentsMargins();
333 //we need to subtract the contents margin (it will be added by the caller)
334 QSize min = w->minimumSize().shrunkBy(m: margins);
335 QSize max = w->maximumSize().shrunkBy(m: margins);
336
337 /* A floating dockwidget will automatically get its minimumSize set to the layout's
338 minimum size + deco. We're *not* interested in this, we only take minimumSize()
339 into account if the user set it herself. Otherwise we end up expanding the result
340 of a calculation for a non-floating dock widget to a floating dock widget's
341 minimum size + window decorations. */
342
343 uint explicitMin = 0;
344 uint explicitMax = 0;
345 if (w->d_func()->extra != nullptr) {
346 explicitMin = w->d_func()->extra->explicitMinSize;
347 explicitMax = w->d_func()->extra->explicitMaxSize;
348 }
349
350 if (!(explicitMin & Qt::Horizontal) || min.width() == 0)
351 min.setWidth(-1);
352 if (!(explicitMin & Qt::Vertical) || min.height() == 0)
353 min.setHeight(-1);
354
355 if (!(explicitMax & Qt::Horizontal))
356 max.setWidth(QWIDGETSIZE_MAX);
357 if (!(explicitMax & Qt::Vertical))
358 max.setHeight(QWIDGETSIZE_MAX);
359
360 return result.boundedTo(otherSize: max).expandedTo(otherSize: min);
361}
362
363QSize QDockWidgetLayout::sizeHint() const
364{
365 QDockWidget *w = qobject_cast<QDockWidget*>(object: parentWidget());
366
367 QSize content(-1, -1);
368 if (item_list[Content] != 0)
369 content = item_list[Content]->sizeHint();
370
371 return sizeFromContent(content, floating: w->isFloating());
372}
373
374QSize QDockWidgetLayout::maximumSize() const
375{
376 if (item_list[Content] != 0) {
377 const QSize content = item_list[Content]->maximumSize();
378 return sizeFromContent(content, floating: parentWidget()->isWindow());
379 } else {
380 return parentWidget()->maximumSize();
381 }
382
383}
384
385QSize QDockWidgetLayout::minimumSize() const
386{
387 QDockWidget *w = qobject_cast<QDockWidget*>(object: parentWidget());
388
389 QSize content(0, 0);
390 if (item_list[Content] != 0)
391 content = item_list[Content]->minimumSize();
392
393 return sizeFromContent(content, floating: w->isFloating());
394}
395
396QWidget *QDockWidgetLayout::widgetForRole(Role r) const
397{
398 QLayoutItem *item = item_list.at(i: r);
399 return item == nullptr ? nullptr : item->widget();
400}
401
402QLayoutItem *QDockWidgetLayout::itemForRole(Role r) const
403{
404 return item_list.at(i: r);
405}
406
407void QDockWidgetLayout::setWidgetForRole(Role r, QWidget *w)
408{
409 QWidget *old = widgetForRole(r);
410 if (old != nullptr) {
411 old->hide();
412 removeWidget(w: old);
413 }
414
415 if (w != nullptr) {
416 addChildWidget(w);
417 item_list[r] = new QWidgetItemV2(w);
418 w->show();
419 } else {
420 item_list[r] = 0;
421 }
422
423 invalidate();
424}
425
426static inline int pick(bool vertical, const QSize &size)
427{
428 return vertical ? size.height() : size.width();
429}
430
431static inline int perp(bool vertical, const QSize &size)
432{
433 return vertical ? size.width() : size.height();
434}
435
436int QDockWidgetLayout::minimumTitleWidth() const
437{
438 QDockWidget *q = qobject_cast<QDockWidget*>(object: parentWidget());
439
440 if (QWidget *title = widgetForRole(r: TitleBar))
441 return pick(vertical: verticalTitleBar, size: title->minimumSizeHint());
442
443 QSize closeSize(0, 0);
444 QSize floatSize(0, 0);
445 if (hasFeature(dockwidget: q, feature: QDockWidget::DockWidgetClosable)) {
446 if (QLayoutItem *item = item_list[CloseButton])
447 closeSize = item->widget()->sizeHint();
448 }
449 if (hasFeature(dockwidget: q, feature: QDockWidget::DockWidgetFloatable)) {
450 if (QLayoutItem *item = item_list[FloatButton])
451 floatSize = item->widget()->sizeHint();
452 }
453
454 int titleHeight = this->titleHeight();
455
456 int mw = q->style()->pixelMetric(metric: QStyle::PM_DockWidgetTitleMargin, option: nullptr, widget: q);
457 int fw = q->style()->pixelMetric(metric: QStyle::PM_DockWidgetFrameWidth, option: nullptr, widget: q);
458
459 return pick(vertical: verticalTitleBar, size: closeSize)
460 + pick(vertical: verticalTitleBar, size: floatSize)
461 + titleHeight + 2*fw + 3*mw;
462}
463
464int QDockWidgetLayout::titleHeight() const
465{
466 QDockWidget *q = qobject_cast<QDockWidget*>(object: parentWidget());
467
468 if (QWidget *title = widgetForRole(r: TitleBar))
469 return perp(vertical: verticalTitleBar, size: title->sizeHint());
470
471 QSize closeSize(0, 0);
472 QSize floatSize(0, 0);
473 if (QLayoutItem *item = item_list[CloseButton])
474 closeSize = item->widget()->sizeHint();
475 if (QLayoutItem *item = item_list[FloatButton])
476 floatSize = item->widget()->sizeHint();
477
478 int buttonHeight = qMax(a: perp(vertical: verticalTitleBar, size: closeSize),
479 b: perp(vertical: verticalTitleBar, size: floatSize));
480
481 QFontMetrics titleFontMetrics = q->fontMetrics();
482 int mw = q->style()->pixelMetric(metric: QStyle::PM_DockWidgetTitleMargin, option: nullptr, widget: q);
483
484 return qMax(a: buttonHeight + 2, b: titleFontMetrics.height() + 2*mw);
485}
486
487void QDockWidgetLayout::setGeometry(const QRect &geometry)
488{
489 QDockWidget *q = qobject_cast<QDockWidget*>(object: parentWidget());
490
491 bool nativeDeco = nativeWindowDeco();
492
493 int fw = q->isFloating() && !nativeDeco
494 ? q->style()->pixelMetric(metric: QStyle::PM_DockWidgetFrameWidth, option: nullptr, widget: q)
495 : 0;
496
497 if (nativeDeco) {
498 if (QLayoutItem *item = item_list[Content])
499 item->setGeometry(geometry);
500 } else {
501 int titleHeight = this->titleHeight();
502
503 if (verticalTitleBar) {
504 _titleArea = QRect(QPoint(fw, fw),
505 QSize(titleHeight, geometry.height() - (fw * 2)));
506 } else {
507 _titleArea = QRect(QPoint(fw, fw),
508 QSize(geometry.width() - (fw * 2), titleHeight));
509 }
510
511 if (QLayoutItem *item = item_list[TitleBar]) {
512 item->setGeometry(_titleArea);
513 } else {
514 QStyleOptionDockWidget opt;
515 q->initStyleOption(option: &opt);
516
517 if (QLayoutItem *item = item_list[CloseButton]) {
518 if (!item->isEmpty()) {
519 QRect r = q->style()
520 ->subElementRect(subElement: QStyle::SE_DockWidgetCloseButton,
521 option: &opt, widget: q);
522 if (!r.isNull())
523 item->setGeometry(r);
524 }
525 }
526
527 if (QLayoutItem *item = item_list[FloatButton]) {
528 if (!item->isEmpty()) {
529 QRect r = q->style()
530 ->subElementRect(subElement: QStyle::SE_DockWidgetFloatButton,
531 option: &opt, widget: q);
532 if (!r.isNull())
533 item->setGeometry(r);
534 }
535 }
536 }
537
538 if (QLayoutItem *item = item_list[Content]) {
539 QRect r = geometry;
540 if (verticalTitleBar) {
541 r.setLeft(_titleArea.right() + 1);
542 r.adjust(dx1: 0, dy1: fw, dx2: -fw, dy2: -fw);
543 } else {
544 r.setTop(_titleArea.bottom() + 1);
545 r.adjust(dx1: fw, dy1: 0, dx2: -fw, dy2: -fw);
546 }
547 item->setGeometry(r);
548 }
549 }
550}
551
552void QDockWidgetLayout::setVerticalTitleBar(bool b)
553{
554 if (b == verticalTitleBar)
555 return;
556 verticalTitleBar = b;
557 invalidate();
558 parentWidget()->update();
559}
560
561/******************************************************************************
562** QDockWidgetItem
563*/
564
565QDockWidgetItem::QDockWidgetItem(QDockWidget *dockWidget)
566 : QWidgetItem(dockWidget)
567{
568}
569
570QSize QDockWidgetItem::minimumSize() const
571{
572 QSize widgetMin(0, 0);
573 if (QLayoutItem *item = dockWidgetChildItem())
574 widgetMin = item->minimumSize();
575 return dockWidgetLayout()->sizeFromContent(content: widgetMin, floating: false);
576}
577
578QSize QDockWidgetItem::maximumSize() const
579{
580 if (QLayoutItem *item = dockWidgetChildItem()) {
581 return dockWidgetLayout()->sizeFromContent(content: item->maximumSize(), floating: false);
582 } else {
583 return QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
584 }
585}
586
587
588QSize QDockWidgetItem::sizeHint() const
589{
590 if (QLayoutItem *item = dockWidgetChildItem()) {
591 return dockWidgetLayout()->sizeFromContent(content: item->sizeHint(), floating: false);
592 } else {
593 return QWidgetItem::sizeHint();
594 }
595}
596
597/******************************************************************************
598** QDockWidgetPrivate
599*/
600
601void QDockWidgetPrivate::init()
602{
603 Q_Q(QDockWidget);
604
605 QDockWidgetLayout *layout = new QDockWidgetLayout(q);
606 layout->setSizeConstraint(QLayout::SetMinAndMaxSize);
607
608 QAbstractButton *button = new QDockWidgetTitleButton(q);
609 button->setObjectName("qt_dockwidget_floatbutton"_L1);
610 QObjectPrivate::connect(sender: button, signal: &QAbstractButton::clicked,
611 receiverPrivate: this, slot: &QDockWidgetPrivate::toggleTopLevel);
612 layout->setWidgetForRole(r: QDockWidgetLayout::FloatButton, w: button);
613
614 button = new QDockWidgetTitleButton(q);
615 button->setObjectName("qt_dockwidget_closebutton"_L1);
616 QObject::connect(sender: button, signal: &QAbstractButton::clicked, context: q, slot: &QDockWidget::close);
617 layout->setWidgetForRole(r: QDockWidgetLayout::CloseButton, w: button);
618
619 font = QApplication::font(className: "QDockWidgetTitle");
620
621#ifndef QT_NO_ACTION
622 toggleViewAction = new QAction(q);
623 toggleViewAction->setCheckable(true);
624 toggleViewAction->setMenuRole(QAction::NoRole);
625 fixedWindowTitle = qt_setWindowTitle_helperHelper(q->windowTitle(), q);
626 toggleViewAction->setText(fixedWindowTitle);
627 QObjectPrivate::connect(sender: toggleViewAction, signal: &QAction::triggered,
628 receiverPrivate: this, slot: &QDockWidgetPrivate::toggleView);
629#endif
630
631 updateButtons();
632}
633
634/*!
635 Initialize \a option with the values from this QDockWidget. This method
636 is useful for subclasses when they need a QStyleOptionDockWidget, but don't want
637 to fill in all the information themselves.
638
639 \sa QStyleOption::initFrom()
640*/
641void QDockWidget::initStyleOption(QStyleOptionDockWidget *option) const
642{
643 Q_D(const QDockWidget);
644
645 if (!option)
646 return;
647 QDockWidgetLayout *dwlayout = qobject_cast<QDockWidgetLayout*>(object: layout());
648
649 QDockWidgetGroupWindow *floatingTab = qobject_cast<QDockWidgetGroupWindow*>(object: parent());
650 // If we are in a floating tab, init from the parent because the attributes and the geometry
651 // of the title bar should be taken from the floating window.
652 option->initFrom(w: floatingTab && !isFloating() ? parentWidget() : this);
653 option->rect = dwlayout->titleArea();
654 option->title = d->fixedWindowTitle;
655 option->closable = hasFeature(dockwidget: this, feature: QDockWidget::DockWidgetClosable);
656 option->movable = hasFeature(dockwidget: this, feature: QDockWidget::DockWidgetMovable);
657 option->floatable = hasFeature(dockwidget: this, feature: QDockWidget::DockWidgetFloatable);
658
659 QDockWidgetLayout *l = qobject_cast<QDockWidgetLayout*>(object: layout());
660 option->verticalTitleBar = l->verticalTitleBar;
661}
662
663void QDockWidgetPrivate::toggleView(bool b)
664{
665 Q_Q(QDockWidget);
666 if (b == q->isHidden()) {
667 if (b)
668 q->show();
669 else
670 q->close();
671 }
672}
673
674void QDockWidgetPrivate::updateButtons()
675{
676 Q_Q(QDockWidget);
677 QDockWidgetLayout *dwLayout = qobject_cast<QDockWidgetLayout*>(object: layout);
678
679 QStyleOptionDockWidget opt;
680 q->initStyleOption(option: &opt);
681
682 bool customTitleBar = dwLayout->widgetForRole(r: QDockWidgetLayout::TitleBar) != nullptr;
683 bool nativeDeco = dwLayout->nativeWindowDeco();
684 bool hideButtons = nativeDeco || customTitleBar;
685
686 bool canClose = hasFeature(priv: this, feature: QDockWidget::DockWidgetClosable);
687 bool canFloat = hasFeature(priv: this, feature: QDockWidget::DockWidgetFloatable);
688
689 QAbstractButton *button
690 = qobject_cast<QAbstractButton*>(object: dwLayout->widgetForRole(r: QDockWidgetLayout::FloatButton));
691 button->setIcon(q->style()->standardIcon(standardIcon: QStyle::SP_TitleBarNormalButton, option: &opt, widget: q));
692 button->setVisible(canFloat && !hideButtons);
693#if QT_CONFIG(accessibility)
694 //: Accessible name for button undocking a dock widget (floating state)
695 button->setAccessibleName(QDockWidget::tr(s: "Float"));
696 button->setAccessibleDescription(QDockWidget::tr(s: "Undocks and re-attaches the dock widget"));
697#endif
698 button
699 = qobject_cast <QAbstractButton*>(object: dwLayout->widgetForRole(r: QDockWidgetLayout::CloseButton));
700 button->setIcon(q->style()->standardIcon(standardIcon: QStyle::SP_TitleBarCloseButton, option: &opt, widget: q));
701 button->setVisible(canClose && !hideButtons);
702#if QT_CONFIG(accessibility)
703 //: Accessible name for button closing a dock widget
704 button->setAccessibleName(QDockWidget::tr(s: "Close"));
705 button->setAccessibleDescription(QDockWidget::tr(s: "Closes the dock widget"));
706#endif
707
708 layout->invalidate();
709}
710
711void QDockWidgetPrivate::toggleTopLevel()
712{
713 Q_Q(QDockWidget);
714 q->setFloating(!q->isFloating());
715}
716
717/*! \internal
718 Initialize the drag state structure and remember the position of the click.
719 This is called when the mouse is pressed, but the dock is not yet dragged out.
720
721 \a nca specify that the event comes from NonClientAreaMouseButtonPress
722 */
723void QDockWidgetPrivate::initDrag(const QPoint &pos, bool nca)
724{
725 Q_Q(QDockWidget);
726
727 if (state != nullptr)
728 return;
729
730 QMainWindowLayout *layout = qt_mainwindow_layout_from_dock(dock: q);
731 Q_ASSERT(layout != nullptr);
732 if (layout->pluggingWidget != nullptr) // the main window is animating a docking operation
733 return;
734
735 state = new QDockWidgetPrivate::DragState;
736 state->pressPos = pos;
737 state->globalPressPos = q->mapToGlobal(pos);
738 state->widgetInitialPos = q->isFloating() ? q->pos() : q->mapToGlobal(QPoint(0, 0));
739 state->dragging = false;
740 state->widgetItem = nullptr;
741 state->ownWidgetItem = false;
742 state->nca = nca;
743 state->ctrlDrag = false;
744}
745
746/*! \internal
747 Actually start the drag and detach the dockwidget.
748 The \a group parameter is true when we should potentially drag a group of
749 tabbed widgets, and false if the dock widget should always be dragged
750 alone.
751 */
752void QDockWidgetPrivate::startDrag(DragScope scope)
753{
754 Q_Q(QDockWidget);
755
756 if (state == nullptr || state->dragging)
757 return;
758
759 QMainWindowLayout *layout = qt_mainwindow_layout_from_dock(dock: q);
760 Q_ASSERT(layout != nullptr);
761
762#if QT_CONFIG(draganddrop)
763 bool wasFloating = q->isFloating();
764#endif
765
766 state->widgetItem = layout->unplug(widget: q, scope);
767 if (state->widgetItem == nullptr) {
768 /* Dock widget has a QMainWindow parent, but was never inserted with
769 QMainWindow::addDockWidget, so the QMainWindowLayout has no
770 widget item for it. It will be newly created and deleted if it doesn't
771 get dropped into a dock area. */
772 QDockWidgetGroupWindow *floatingTab = qobject_cast<QDockWidgetGroupWindow*>(object: parent);
773 if (floatingTab && !q->isFloating())
774 state->widgetItem = new QDockWidgetGroupWindowItem(floatingTab);
775 else
776 state->widgetItem = new QDockWidgetItem(q);
777 state->ownWidgetItem = true;
778 }
779
780 if (state->ctrlDrag)
781 layout->restore();
782
783 state->dragging = true;
784
785#if QT_CONFIG(draganddrop)
786 if (QMainWindowLayout::needsPlatformDrag()) {
787 Qt::DropAction result =
788 layout->performPlatformWidgetDrag(widgetItem: state->widgetItem, pressPosition: state->pressPos);
789 if (result == Qt::IgnoreAction && !wasFloating) {
790 layout->revert(widgetItem: state->widgetItem);
791 delete state;
792 state = nullptr;
793 } else {
794 endDrag(mode: QDockWidgetPrivate::EndDragMode::LocationChange);
795 }
796 }
797#endif
798}
799
800/*! \internal
801 Ends the drag end drop operation of the QDockWidget.
802 The \a abort parameter specifies that it ends because of programmatic state
803 reset rather than mouse release event.
804 */
805void QDockWidgetPrivate::endDrag(EndDragMode mode)
806{
807 Q_Q(QDockWidget);
808 Q_ASSERT(state != nullptr);
809
810 q->releaseMouse();
811
812 if (state->dragging) {
813 const QMainWindow *mainWindow = mainwindow_from_dock(dock: q);
814 Q_ASSERT(mainWindow != nullptr);
815 QMainWindowLayout *mwLayout = qt_mainwindow_layout(window: mainWindow);
816
817 // if mainWindow is being deleted in an ongoing drag, make it a no-op instead of crashing
818 if (!mwLayout)
819 return;
820
821 if (mode == EndDragMode::Abort || !mwLayout->plug(widgetItem: state->widgetItem)) {
822 if (hasFeature(priv: this, feature: QDockWidget::DockWidgetFloatable)) {
823 // This QDockWidget will now stay in the floating state.
824 if (state->ownWidgetItem) {
825 delete state->widgetItem;
826 state->widgetItem = nullptr;
827 }
828 mwLayout->restore();
829 QDockWidgetLayout *dwLayout = qobject_cast<QDockWidgetLayout*>(object: layout);
830 if (!dwLayout->nativeWindowDeco()) {
831 // get rid of the X11BypassWindowManager window flag and activate the resizer
832 Qt::WindowFlags flags = q->windowFlags();
833 flags &= ~Qt::X11BypassWindowManagerHint;
834 q->setWindowFlags(flags);
835 setResizerActive(q->isFloating());
836 q->show();
837 } else {
838 setResizerActive(false);
839 }
840 if (q->isFloating()) { // Might not be floating when dragging a QDockWidgetGroupWindow
841 undockedGeometry = q->geometry();
842#if QT_CONFIG(tabwidget)
843 // is the widget located within the mainwindow?
844 const Qt::DockWidgetArea area = mainWindow->dockWidgetArea(dockwidget: q);
845 if (area != Qt::NoDockWidgetArea) {
846 tabPosition = mwLayout->tabPosition(area);
847 } else if (auto dwgw = qobject_cast<QDockWidgetGroupWindow *>(object: q->parent())) {
848 // DockWidget wasn't found in one of the docks within mainwindow
849 // => derive tabPosition from parent
850 tabPosition = mwLayout->tabPosition(area: toDockWidgetArea(pos: dwgw->layoutInfo()->dockPos));
851 }
852#endif
853 // Reparent, if the drag was out of a dock widget group window
854 if (mode == EndDragMode::LocationChange) {
855 if (auto *groupWindow = qobject_cast<QDockWidgetGroupWindow *>(object: q->parentWidget()))
856 groupWindow->reparentToMainWindow(dockWidget: q);
857 }
858 }
859 q->activateWindow();
860 } else {
861 // The tab was not plugged back in the QMainWindow but the QDockWidget cannot
862 // stay floating, revert to the previous state.
863 mwLayout->revert(widgetItem: state->widgetItem);
864 }
865 }
866 }
867 delete state;
868 state = nullptr;
869}
870
871Qt::DockWidgetArea QDockWidgetPrivate::toDockWidgetArea(QInternal::DockPosition pos)
872{
873 switch (pos) {
874 case QInternal::LeftDock: return Qt::LeftDockWidgetArea;
875 case QInternal::RightDock: return Qt::RightDockWidgetArea;
876 case QInternal::TopDock: return Qt::TopDockWidgetArea;
877 case QInternal::BottomDock: return Qt::BottomDockWidgetArea;
878 default: break;
879 }
880 return Qt::NoDockWidgetArea;
881}
882
883void QDockWidgetPrivate::setResizerActive(bool active)
884{
885 Q_Q(QDockWidget);
886 const auto *dwLayout = qobject_cast<QDockWidgetLayout *>(object: layout);
887 if (dwLayout->nativeWindowDeco(floating: q->isFloating()))
888 return;
889
890 if (active && !resizer)
891 resizer = new QWidgetResizeHandler(q);
892 if (resizer)
893 resizer->setEnabled(active);
894}
895
896bool QDockWidgetPrivate::isAnimating() const
897{
898 Q_Q(const QDockWidget);
899
900 QMainWindowLayout *mainWinLayout = qt_mainwindow_layout_from_dock(dock: q);
901 if (mainWinLayout == nullptr)
902 return false;
903
904 return (const void*)mainWinLayout->pluggingWidget == (const void*)q;
905}
906
907bool QDockWidgetPrivate::mousePressEvent(QMouseEvent *event)
908{
909#if QT_CONFIG(mainwindow)
910 Q_Q(QDockWidget);
911
912 QDockWidgetLayout *dwLayout
913 = qobject_cast<QDockWidgetLayout*>(object: layout);
914
915 if (!dwLayout->nativeWindowDeco()) {
916 QRect titleArea = dwLayout->titleArea();
917
918 QDockWidgetGroupWindow *floatingTab = qobject_cast<QDockWidgetGroupWindow*>(object: parent);
919
920 if (event->button() != Qt::LeftButton ||
921 !titleArea.contains(p: event->position().toPoint()) ||
922 // check if the tool window is movable... do nothing if it
923 // is not (but allow moving if the window is floating)
924 (!hasFeature(priv: this, feature: QDockWidget::DockWidgetMovable) && !q->isFloating()) ||
925 (qobject_cast<QMainWindow*>(object: parent) == nullptr && !floatingTab) ||
926 isAnimating() || state != nullptr) {
927 return false;
928 }
929
930 initDrag(pos: event->position().toPoint(), nca: false);
931
932 if (state)
933 state->ctrlDrag = (hasFeature(priv: this, feature: QDockWidget::DockWidgetFloatable) && event->modifiers() & Qt::ControlModifier) ||
934 (!hasFeature(priv: this, feature: QDockWidget::DockWidgetMovable) && q->isFloating());
935
936 return true;
937 }
938
939#endif // QT_CONFIG(mainwindow)
940 return false;
941}
942
943bool QDockWidgetPrivate::mouseDoubleClickEvent(QMouseEvent *event)
944{
945 QDockWidgetLayout *dwLayout = qobject_cast<QDockWidgetLayout*>(object: layout);
946
947 if (!dwLayout->nativeWindowDeco()) {
948 QRect titleArea = dwLayout->titleArea();
949
950 if (event->button() == Qt::LeftButton && titleArea.contains(p: event->position().toPoint()) &&
951 hasFeature(priv: this, feature: QDockWidget::DockWidgetFloatable)) {
952 toggleTopLevel();
953 return true;
954 }
955 }
956 return false;
957}
958
959bool QDockWidgetPrivate::isTabbed() const
960{
961 Q_Q(const QDockWidget);
962 QDockWidget *that = const_cast<QDockWidget *>(q);
963 auto *mwLayout = qt_mainwindow_layout_from_dock(dock: that);
964 Q_ASSERT(mwLayout);
965 return mwLayout->isDockWidgetTabbed(dockWidget: q);
966}
967
968bool QDockWidgetPrivate::mouseMoveEvent(QMouseEvent *event)
969{
970 bool ret = false;
971#if QT_CONFIG(mainwindow)
972 Q_Q(QDockWidget);
973
974 if (!state)
975 return ret;
976
977 QDockWidgetLayout *dwlayout
978 = qobject_cast<QDockWidgetLayout *>(object: layout);
979 QMainWindowLayout *mwlayout = qt_mainwindow_layout_from_dock(dock: q);
980 if (!dwlayout->nativeWindowDeco()) {
981 if (!state->dragging
982 && mwlayout->pluggingWidget == nullptr
983 && (event->position().toPoint() - state->pressPos).manhattanLength()
984 > QApplication::startDragDistance()) {
985
986#ifdef Q_OS_MACOS
987 if (windowHandle() && !q->isFloating()) {
988 // When using native widgets on mac, we have not yet been successful in
989 // starting a drag on an NSView that belongs to one window (QMainWindow),
990 // but continue the drag on another (QDockWidget). This is what happens if
991 // we try to make this widget floating during a drag. So as a fall back
992 // solution, we simply make this widget floating instead, when we would
993 // otherwise start a drag.
994 q->setFloating(true);
995 } else
996#endif
997 {
998 const DragScope scope = isTabbed() ? DragScope::Group : DragScope::Widget;
999 startDrag(scope);
1000 q->grabMouse();
1001 ret = true;
1002 }
1003 }
1004 }
1005
1006 if (state && state->dragging && !state->nca) {
1007 QMargins windowMargins = q->window()->windowHandle()->frameMargins();
1008 QPoint windowMarginOffset = QPoint(windowMargins.left(), windowMargins.top());
1009
1010 // TODO maybe use QScreen API (if/when available) to simplify the below code.
1011 const QScreen *orgWdgScreen = QGuiApplication::screenAt(point: state->widgetInitialPos);
1012 const QScreen *screenFrom = QGuiApplication::screenAt(point: state->globalPressPos);
1013 const QScreen *screenTo = QGuiApplication::screenAt(point: event->globalPosition().toPoint());
1014 const QScreen *wdgScreen = q->screen();
1015
1016 QPoint pos;
1017 if (Q_LIKELY(screenFrom && screenTo && wdgScreen && orgWdgScreen)) {
1018 const QPoint nativeWdgOrgPos = QHighDpiScaling::mapPositionToNative(
1019 pos: state->widgetInitialPos, platformScreen: orgWdgScreen->handle());
1020 const QPoint nativeTo = QHighDpiScaling::mapPositionToNative(
1021 pos: event->globalPosition().toPoint(), platformScreen: screenTo->handle());
1022 const QPoint nativeFrom = QHighDpiScaling::mapPositionToNative(pos: state->globalPressPos,
1023 platformScreen: screenFrom->handle());
1024
1025 // Calculate new nativePos based on startPos + mouse delta move.
1026 const QPoint nativeNewPos = nativeWdgOrgPos + (nativeTo - nativeFrom);
1027 pos = QHighDpiScaling::mapPositionFromNative(pos: nativeNewPos, platformScreen: wdgScreen->handle())
1028 - windowMarginOffset;
1029 } else {
1030 // Fallback in the unlikely case that source and target screens could not be established
1031 qCDebug(lcQpaDockWidgets)
1032 << "QDockWidget failed to find relevant screen info. screenFrom:" << screenFrom
1033 << "screenTo:" << screenTo << " wdgScreen:" << wdgScreen << "orgWdgScreen"
1034 << orgWdgScreen;
1035 pos = event->globalPosition().toPoint() - state->pressPos - windowMarginOffset;
1036 }
1037
1038 // If the newly floating dock widget has got a native title bar,
1039 // offset the position by the native title bar's height or width
1040 const int dx = q->geometry().x() - q->x();
1041 const int dy = q->geometry().y() - q->y();
1042 pos.rx() += dx;
1043 pos.ry() += dy;
1044
1045 QDockWidgetGroupWindow *floatingTab = qobject_cast<QDockWidgetGroupWindow*>(object: parent);
1046 if (floatingTab && !q->isFloating())
1047 floatingTab->move(pos);
1048 else
1049 q->move(pos);
1050 if (state && !state->ctrlDrag)
1051 mwlayout->hover(hoverTarget: state->widgetItem, mousePos: event->globalPosition().toPoint());
1052
1053 ret = true;
1054 }
1055
1056#endif // QT_CONFIG(mainwindow)
1057 return ret;
1058}
1059
1060bool QDockWidgetPrivate::mouseReleaseEvent(QMouseEvent *event)
1061{
1062#if QT_CONFIG(mainwindow)
1063#if QT_CONFIG(draganddrop)
1064 // if we are peforming a platform drag ignore the release here and end the drag when the actual
1065 // drag ends.
1066 if (QMainWindowLayout::needsPlatformDrag())
1067 return false;
1068#endif
1069
1070 if (event->button() == Qt::LeftButton && state && !state->nca) {
1071 endDrag(mode: EndDragMode::LocationChange);
1072 return true; //filter out the event
1073 }
1074
1075#endif // QT_CONFIG(mainwindow)
1076 return false;
1077}
1078
1079void QDockWidgetPrivate::nonClientAreaMouseEvent(QMouseEvent *event)
1080{
1081 Q_Q(QDockWidget);
1082
1083 int fw = q->style()->pixelMetric(metric: QStyle::PM_DockWidgetFrameWidth, option: nullptr, widget: q);
1084
1085 QWidget *tl = q->topLevelWidget();
1086 QRect geo = tl->geometry();
1087 QRect titleRect = tl->frameGeometry();
1088 {
1089 titleRect.setLeft(geo.left());
1090 titleRect.setRight(geo.right());
1091 titleRect.setBottom(geo.top() - 1);
1092 titleRect.adjust(dx1: 0, dy1: fw, dx2: 0, dy2: 0);
1093 }
1094
1095 switch (event->type()) {
1096 case QEvent::NonClientAreaMouseButtonPress:
1097 if (!titleRect.contains(p: event->globalPosition().toPoint()))
1098 break;
1099 if (state != nullptr)
1100 break;
1101 if (qobject_cast<QMainWindow*>(object: parent) == nullptr && qobject_cast<QDockWidgetGroupWindow*>(object: parent) == nullptr)
1102 break;
1103 if (isAnimating())
1104 break;
1105 initDrag(pos: event->position().toPoint(), nca: true);
1106 if (state == nullptr)
1107 break;
1108 state->ctrlDrag = (event->modifiers() & Qt::ControlModifier) ||
1109 (!hasFeature(priv: this, feature: QDockWidget::DockWidgetMovable) && q->isFloating());
1110 startDrag(scope: DragScope::Group);
1111 break;
1112 case QEvent::NonClientAreaMouseMove:
1113 if (state == nullptr || !state->dragging)
1114 break;
1115
1116#if !defined(Q_OS_MAC) && !defined(Q_OS_WASM)
1117 if (state->nca)
1118 endDrag(mode: EndDragMode::LocationChange);
1119#endif
1120 break;
1121 case QEvent::NonClientAreaMouseButtonRelease:
1122#if defined(Q_OS_MAC) || defined(Q_OS_WASM)
1123 if (state)
1124 endDrag(EndDragMode::LocationChange);
1125#endif
1126 break;
1127 case QEvent::NonClientAreaMouseButtonDblClick:
1128 toggleTopLevel();
1129 break;
1130 default:
1131 break;
1132 }
1133}
1134
1135void QDockWidgetPrivate::recalculatePressPos(QResizeEvent *event)
1136{
1137 qreal ratio = event->oldSize().width() / (1.0 * event->size().width());
1138 state->pressPos.setX(state->pressPos.x() / ratio);
1139}
1140
1141/*! \internal
1142 Called when the QDockWidget or the QDockWidgetGroupWindow is moved
1143 */
1144void QDockWidgetPrivate::moveEvent(QMoveEvent *event)
1145{
1146 Q_Q(QDockWidget);
1147
1148 if (state == nullptr || !state->dragging || !state->nca)
1149 return;
1150
1151 if (!q->isWindow() && qobject_cast<QDockWidgetGroupWindow*>(object: parent) == nullptr)
1152 return;
1153
1154 // When the native window frame is being dragged, all we get is these mouse
1155 // move events.
1156
1157 if (state->ctrlDrag)
1158 return;
1159
1160 QMainWindowLayout *layout = qt_mainwindow_layout_from_dock(dock: q);
1161 Q_ASSERT(layout != nullptr);
1162
1163 QPoint globalMousePos = event->pos() + state->pressPos;
1164 layout->hover(hoverTarget: state->widgetItem, mousePos: globalMousePos);
1165}
1166
1167void QDockWidgetPrivate::unplug(const QRect &rect)
1168{
1169 Q_Q(QDockWidget);
1170 QRect r = rect;
1171 r.moveTopLeft(p: q->mapToGlobal(QPoint(0, 0)));
1172 QDockWidgetLayout *dwLayout = qobject_cast<QDockWidgetLayout*>(object: layout);
1173 if (dwLayout->nativeWindowDeco(floating: true))
1174 r.adjust(dx1: 0, dy1: dwLayout->titleHeight(), dx2: 0, dy2: 0);
1175 setWindowState(states: {WindowState::Floating, WindowState::Unplug}, rect: r);
1176}
1177
1178void QDockWidgetPrivate::plug(const QRect &rect)
1179{
1180 setWindowState(states: WindowStates(), rect);
1181}
1182
1183void QDockWidgetPrivate::setWindowState(WindowStates states, const QRect &rect)
1184{
1185 Q_Q(QDockWidget);
1186 const bool floating = states.testFlag(flag: WindowState::Floating);
1187 bool unplug = states.testFlag(flag: WindowState::Unplug);
1188
1189 if (!floating && parent) {
1190 QMainWindowLayout *mwlayout = qt_mainwindow_layout_from_dock(dock: q);
1191 if (mwlayout && mwlayout->dockWidgetArea(widget: q) == Qt::NoDockWidgetArea
1192 && !qobject_cast<QDockWidgetGroupWindow *>(object: parent))
1193 return; // this dockwidget can't be redocked
1194 }
1195
1196 const bool wasFloating = q->isFloating();
1197 if (wasFloating) // Prevent repetitive unplugging from nested invocations (QTBUG-42818)
1198 unplug = false;
1199 const bool hidden = q->isHidden();
1200
1201 if (q->isVisible())
1202 q->hide();
1203
1204 Qt::WindowFlags flags = floating ? Qt::Tool : Qt::Widget;
1205
1206 QDockWidgetLayout *dwLayout = qobject_cast<QDockWidgetLayout*>(object: layout);
1207 const bool nativeDeco = dwLayout->nativeWindowDeco(floating);
1208
1209 if (nativeDeco) {
1210 flags |= Qt::CustomizeWindowHint | Qt::WindowTitleHint;
1211 if (hasFeature(priv: this, feature: QDockWidget::DockWidgetClosable))
1212 flags |= Qt::WindowCloseButtonHint;
1213 } else {
1214 flags |= Qt::FramelessWindowHint;
1215 }
1216
1217#if QT_CONFIG(draganddrop)
1218 // If we are performing a platform drag the flag is not needed and we want to avoid recreating
1219 // the platform window when it would be removed later
1220 if (unplug && !QMainWindowLayout::needsPlatformDrag())
1221 flags |= Qt::X11BypassWindowManagerHint;
1222#endif
1223
1224 q->setWindowFlags(flags);
1225
1226
1227 if (!rect.isNull())
1228 q->setGeometry(rect);
1229
1230 updateButtons();
1231
1232 if (!hidden)
1233 q->show();
1234
1235 if (floating != wasFloating) {
1236 emit q->topLevelChanged(topLevel: floating);
1237 if (!floating && parent) {
1238 QMainWindowLayout *mwlayout = qt_mainwindow_layout_from_dock(dock: q);
1239 if (mwlayout)
1240 emit q->dockLocationChanged(area: mwlayout->dockWidgetArea(widget: q));
1241 } else {
1242 emit q->dockLocationChanged(area: Qt::NoDockWidgetArea);
1243 }
1244 }
1245
1246 setResizerActive(!unplug && floating && !nativeDeco);
1247}
1248
1249/*!
1250 \class QDockWidget
1251
1252 \brief The QDockWidget class provides a widget that can be docked
1253 inside a QMainWindow or floated as a top-level window on the
1254 desktop.
1255
1256 \ingroup mainwindow-classes
1257 \inmodule QtWidgets
1258
1259 QDockWidget provides the concept of dock widgets, also know as
1260 tool palettes or utility windows. Dock windows are secondary
1261 windows placed in the \e {dock widget area} around the
1262 \l{QMainWindow::centralWidget()}{central widget} in a
1263 QMainWindow.
1264
1265 \image mainwindow-docks.png
1266
1267 Dock windows can be moved inside their current area, moved into
1268 new areas and floated (e.g., undocked) by the end-user. The
1269 QDockWidget API allows the programmer to restrict the dock widgets
1270 ability to move, float and close, as well as the areas in which
1271 they can be placed.
1272
1273 \section1 Appearance
1274
1275 A QDockWidget consists of a title bar and the content area. The
1276 title bar displays the dock widgets
1277 \l{QWidget::windowTitle()}{window title},
1278 a \e float button and a \e close button.
1279 Depending on the state of the QDockWidget, the \e float and \e
1280 close buttons may be either disabled or not shown at all.
1281
1282 The visual appearance of the title bar and buttons is dependent
1283 on the \l{QStyle}{style} in use.
1284
1285 A QDockWidget acts as a wrapper for its child widget, set with setWidget().
1286 Custom size hints, minimum and maximum sizes and size policies should be
1287 implemented in the child widget. QDockWidget will respect them, adjusting
1288 its own constraints to include the frame and title. Size constraints
1289 should not be set on the QDockWidget itself, because they change depending
1290 on whether it is docked; a docked QDockWidget has no frame and a smaller title
1291 bar.
1292
1293 \note On macOS, if the QDockWidget has a native window handle (for example,
1294 if winId() is called on it or the child widget), then due to a limitation it will not be
1295 possible to drag the dock widget when undocking. Starting the drag will undock
1296 the dock widget, but a second drag will be needed to move the dock widget itself.
1297
1298 \sa QMainWindow
1299*/
1300
1301/*!
1302 \enum QDockWidget::DockWidgetFeature
1303
1304 \value DockWidgetClosable The dock widget can be closed.
1305 \value DockWidgetMovable The dock widget can be moved between docks
1306 by the user.
1307 \value DockWidgetFloatable The dock widget can be detached from the
1308 main window, and floated as an independent
1309 window.
1310 \value DockWidgetVerticalTitleBar The dock widget displays a vertical title
1311 bar on its left side. This can be used to
1312 increase the amount of vertical space in
1313 a QMainWindow.
1314 \value NoDockWidgetFeatures The dock widget cannot be closed, moved,
1315 or floated.
1316
1317 \omitvalue DockWidgetFeatureMask
1318 \omitvalue Reserved
1319*/
1320
1321/*!
1322 \property QDockWidget::windowTitle
1323 \brief the dock widget title (caption)
1324
1325 By default, this property contains an empty string.
1326*/
1327
1328/*!
1329 Constructs a QDockWidget with parent \a parent and window flags \a
1330 flags. The dock widget will be placed in the left dock widget
1331 area.
1332*/
1333QDockWidget::QDockWidget(QWidget *parent, Qt::WindowFlags flags)
1334 : QWidget(*new QDockWidgetPrivate, parent, flags)
1335{
1336 Q_D(QDockWidget);
1337 d->init();
1338}
1339
1340/*!
1341 Constructs a QDockWidget with parent \a parent and window flags \a
1342 flags. The dock widget will be placed in the left dock widget
1343 area.
1344
1345 The window title is set to \a title. This title is used when the
1346 QDockWidget is docked and undocked. It is also used in the context
1347 menu provided by QMainWindow.
1348
1349 \sa setWindowTitle()
1350*/
1351QDockWidget::QDockWidget(const QString &title, QWidget *parent, Qt::WindowFlags flags)
1352 : QDockWidget(parent, flags)
1353{
1354 setWindowTitle(title);
1355}
1356
1357/*!
1358 Destroys the dock widget.
1359*/
1360QDockWidget::~QDockWidget()
1361{
1362 Q_D(QDockWidget);
1363 d->inDestructor = true;
1364 // Do all the unregistering while we're still a QDockWidget. Otherwise, it
1365 // would be ~QObject() which does that and then QDockAreaLayout::takeAt(),
1366 // acting on QEvent::ChildRemoved, will try to access our QWidget-ness when
1367 // replacing us with a QPlaceHolderItem, causing UB:
1368 setParent(nullptr);
1369}
1370
1371/*!
1372 Returns the widget for the dock widget. This function returns zero
1373 if the widget has not been set.
1374
1375 \sa setWidget()
1376*/
1377QWidget *QDockWidget::widget() const
1378{
1379 QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(object: this->layout());
1380 return layout->widgetForRole(r: QDockWidgetLayout::Content);
1381}
1382
1383/*!
1384 Sets the widget for the dock widget to \a widget.
1385
1386 If the dock widget is visible when \a widget is added, you must
1387 \l{QWidget::}{show()} it explicitly.
1388
1389 Note that you must add the layout of the \a widget before you call
1390 this function; if not, the \a widget will not be visible.
1391
1392 \sa widget()
1393*/
1394void QDockWidget::setWidget(QWidget *widget)
1395{
1396 QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(object: this->layout());
1397 layout->setWidgetForRole(r: QDockWidgetLayout::Content, w: widget);
1398}
1399
1400/*!
1401 \property QDockWidget::features
1402 \brief whether the dock widget is movable, closable, and floatable
1403
1404 By default, this property is set to a combination of DockWidgetClosable,
1405 DockWidgetMovable and DockWidgetFloatable.
1406
1407 \sa DockWidgetFeature
1408*/
1409
1410void QDockWidget::setFeatures(QDockWidget::DockWidgetFeatures features)
1411{
1412 Q_D(QDockWidget);
1413 features &= DockWidgetFeatureMask;
1414 if (d->features == features)
1415 return;
1416 const bool closableChanged = (d->features ^ features) & DockWidgetClosable;
1417 d->features = features;
1418 QDockWidgetLayout *layout
1419 = qobject_cast<QDockWidgetLayout*>(object: this->layout());
1420 layout->setVerticalTitleBar(features & DockWidgetVerticalTitleBar);
1421 d->updateButtons();
1422 d->toggleViewAction->setEnabled((d->features & DockWidgetClosable) == DockWidgetClosable);
1423 emit featuresChanged(features: d->features);
1424 update();
1425 if (closableChanged && layout->nativeWindowDeco()) {
1426 QDockWidgetGroupWindow *floatingTab = qobject_cast<QDockWidgetGroupWindow *>(object: parent());
1427 if (floatingTab && !isFloating()) {
1428 floatingTab->adjustFlags();
1429 } else {
1430 d->setWindowState(states: {QDockWidgetPrivate::WindowState::Floating,
1431 QDockWidgetPrivate::WindowState::Unplug});
1432 }
1433 }
1434}
1435
1436QDockWidget::DockWidgetFeatures QDockWidget::features() const
1437{
1438 Q_D(const QDockWidget);
1439 return d->features;
1440}
1441
1442/*!
1443 \property QDockWidget::floating
1444 \brief whether the dock widget is floating
1445
1446 A floating dock widget is presented to the user as a single, independent
1447 window "on top" of its parent QMainWindow, instead of being docked
1448 either in the QMainWindow, or in a group of tabbed dock widgets.
1449
1450 Floating dock widgets can be individually positioned and resized, both
1451 programmatically or by mouse interaction.
1452
1453 By default, this property is \c true.
1454
1455 When this property changes, the \c {topLevelChanged()} signal is emitted.
1456
1457 \sa isWindow(), topLevelChanged()
1458*/
1459void QDockWidget::setFloating(bool floating)
1460{
1461 Q_D(QDockWidget);
1462 d->setFloating(floating);
1463}
1464
1465/*!
1466 \internal implementation of setFloating
1467 */
1468void QDockWidgetPrivate::setFloating(bool floating)
1469{
1470 Q_Q(QDockWidget);
1471 // the initial click of a double-click may have started a drag...
1472 if (state != nullptr)
1473 endDrag(mode: QDockWidgetPrivate::EndDragMode::Abort);
1474
1475 // Keep position when undocking for the first time.
1476 QRect r = undockedGeometry;
1477 if (floating && q->isVisible() && !r.isValid())
1478 r = QRect(q->mapToGlobal(QPoint(0, 0)), q->size());
1479
1480 // Reparent, if setFloating() was called on a floating tab
1481 // Reparenting has to happen before setWindowState.
1482 // The reparented dock widget will inherit visibility from the floating tab.
1483 // => Remember visibility and the necessity to update it.
1484 enum class VisibilityRule {
1485 NoUpdate,
1486 Show,
1487 Hide,
1488 };
1489
1490 VisibilityRule updateRule = VisibilityRule::NoUpdate;
1491
1492 if (floating && !q->isFloating()) {
1493 if (auto *groupWindow = qobject_cast<QDockWidgetGroupWindow *>(object: q->parentWidget())) {
1494 updateRule = groupWindow->isVisible() ? VisibilityRule::Show : VisibilityRule::Hide;
1495 q->setParent(groupWindow->parentWidget());
1496 }
1497 }
1498
1499 WindowStates states;
1500 states.setFlag(flag: WindowState::Floating, on: floating);
1501 setWindowState(states, rect: floating ? r : QRect());
1502
1503 if (floating && r.isNull()) {
1504 if (q->x() < 0 || q->y() < 0) //may happen if we have been hidden
1505 q->move(QPoint());
1506 q->setAttribute(Qt::WA_Moved, on: false); //we want it at the default position
1507 }
1508
1509 switch (updateRule) {
1510 case VisibilityRule::NoUpdate:
1511 break;
1512 case VisibilityRule::Show:
1513 q->show();
1514 break;
1515 case VisibilityRule::Hide:
1516 q->hide();
1517 break;
1518 }
1519}
1520
1521/*!
1522 \property QDockWidget::allowedAreas
1523 \brief areas where the dock widget may be placed
1524
1525 The default is Qt::AllDockWidgetAreas.
1526
1527 \sa Qt::DockWidgetArea
1528*/
1529
1530void QDockWidget::setAllowedAreas(Qt::DockWidgetAreas areas)
1531{
1532 Q_D(QDockWidget);
1533 areas &= Qt::DockWidgetArea_Mask;
1534 if (areas == d->allowedAreas)
1535 return;
1536 d->allowedAreas = areas;
1537 emit allowedAreasChanged(allowedAreas: d->allowedAreas);
1538}
1539
1540Qt::DockWidgetAreas QDockWidget::allowedAreas() const
1541{
1542 Q_D(const QDockWidget);
1543 return d->allowedAreas;
1544}
1545
1546/*!
1547 \fn bool QDockWidget::isAreaAllowed(Qt::DockWidgetArea area) const
1548
1549 Returns \c true if this dock widget can be placed in the given \a area;
1550 otherwise returns \c false.
1551*/
1552
1553/*! \reimp */
1554void QDockWidget::changeEvent(QEvent *event)
1555{
1556 Q_D(QDockWidget);
1557 QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(object: this->layout());
1558
1559 switch (event->type()) {
1560 case QEvent::WindowTitleChange:
1561 if (isFloating() && windowHandle() && d->topData() && windowHandle()->isVisible()) {
1562 // From QWidget::setWindowTitle(): Propagate window title without signal emission
1563 d->topData()->caption = windowHandle()->title();
1564 d->setWindowTitle_helper(windowHandle()->title());
1565 }
1566 Q_FALLTHROUGH();
1567 case QEvent::ModifiedChange:
1568 update(layout->titleArea());
1569#ifndef QT_NO_ACTION
1570 d->fixedWindowTitle = qt_setWindowTitle_helperHelper(windowTitle(), this);
1571 d->toggleViewAction->setText(d->fixedWindowTitle);
1572#endif
1573#if QT_CONFIG(tabbar)
1574 {
1575 if (QMainWindowLayout *winLayout = qt_mainwindow_layout_from_dock(dock: this)) {
1576 if (QDockAreaLayoutInfo *info = winLayout->layoutState.dockAreaLayout.info(widget: this))
1577 info->updateTabBar();
1578 }
1579 }
1580#endif // QT_CONFIG(tabbar)
1581 break;
1582 default:
1583 break;
1584 }
1585 QWidget::changeEvent(event);
1586}
1587
1588/*! \reimp */
1589void QDockWidget::closeEvent(QCloseEvent *event)
1590{
1591 Q_D(QDockWidget);
1592 if (d->state)
1593 d->endDrag(mode: QDockWidgetPrivate::EndDragMode::Abort);
1594
1595 // For non-closable widgets, don't allow closing, except when the mainwindow
1596 // is hidden, as otherwise an application wouldn't be able to be shut down.
1597 const QMainWindow *win = qobject_cast<QMainWindow*>(object: parentWidget());
1598 const bool canClose = (d->features & DockWidgetClosable)
1599 || (!win || !win->isVisible());
1600 event->setAccepted(canClose);
1601}
1602
1603/*! \reimp */
1604void QDockWidget::paintEvent(QPaintEvent *event)
1605{
1606 Q_UNUSED(event);
1607 Q_D(QDockWidget);
1608
1609 QDockWidgetLayout *layout
1610 = qobject_cast<QDockWidgetLayout*>(object: this->layout());
1611 bool customTitleBar = layout->widgetForRole(r: QDockWidgetLayout::TitleBar) != nullptr;
1612 bool nativeDeco = layout->nativeWindowDeco();
1613
1614 if (!nativeDeco && !customTitleBar) {
1615 QStylePainter p(this);
1616 // ### Add PixelMetric to change spacers, so style may show border
1617 // when not floating.
1618 if (isFloating()) {
1619 QStyleOptionFrame framOpt;
1620 framOpt.initFrom(w: this);
1621 p.drawPrimitive(pe: QStyle::PE_FrameDockWidget, opt: framOpt);
1622 }
1623
1624 // Title must be painted after the frame, since the areas overlap, and
1625 // the title may wish to extend out to all sides (eg. Vista style)
1626 QStyleOptionDockWidget titleOpt;
1627 initStyleOption(option: &titleOpt);
1628 if (font() == QApplication::font(className: "QDockWidget")) {
1629 titleOpt.fontMetrics = QFontMetrics(d->font);
1630 p.setFont(d->font);
1631 }
1632
1633 p.drawControl(ce: QStyle::CE_DockWidgetTitle, opt: titleOpt);
1634 }
1635}
1636
1637/*! \reimp */
1638bool QDockWidget::event(QEvent *event)
1639{
1640 Q_D(QDockWidget);
1641
1642 QMainWindow *win = qobject_cast<QMainWindow*>(object: parentWidget());
1643 QMainWindowLayout *layout = qt_mainwindow_layout_from_dock(dock: this);
1644
1645 switch (event->type()) {
1646#ifndef QT_NO_ACTION
1647 case QEvent::Hide:
1648 if (layout != nullptr)
1649 layout->keepSize(w: this);
1650 // If we are in the destructor, don't emit any signals, as those might
1651 // be handled by a slot that requires this dock widget to still be alive.
1652 if (!d->inDestructor) {
1653 d->toggleViewAction->setChecked(false);
1654 emit visibilityChanged(visible: false);
1655 }
1656 break;
1657 case QEvent::Show: {
1658 d->toggleViewAction->setChecked(true);
1659 QPoint parentTopLeft(0, 0);
1660 if (isWindow()) {
1661 const QScreen *screen = d->associatedScreen();
1662 parentTopLeft = screen
1663 ? screen->availableVirtualGeometry().topLeft()
1664 : QGuiApplication::primaryScreen()->availableVirtualGeometry().topLeft();
1665 }
1666 emit visibilityChanged(visible: geometry().right() >= parentTopLeft.x() && geometry().bottom() >= parentTopLeft.y());
1667}
1668 break;
1669#endif
1670 case QEvent::ApplicationLayoutDirectionChange:
1671 case QEvent::LayoutDirectionChange:
1672 case QEvent::StyleChange:
1673 case QEvent::ParentChange:
1674 d->updateButtons();
1675 break;
1676 case QEvent::ZOrderChange: {
1677 bool onTop = false;
1678 if (win != nullptr) {
1679 const QObjectList &siblings = win->children();
1680 onTop = siblings.size() > 0 && siblings.last() == (QObject*)this;
1681 }
1682#if QT_CONFIG(tabbar)
1683 if (!isFloating() && layout != nullptr && onTop)
1684 layout->raise(widget: this);
1685#endif
1686 break;
1687 }
1688 case QEvent::WindowActivate:
1689 case QEvent::WindowDeactivate:
1690 update(qobject_cast<QDockWidgetLayout *>(object: this->layout())->titleArea());
1691 break;
1692 case QEvent::ContextMenu:
1693 if (d->state) {
1694 event->accept();
1695 return true;
1696 }
1697 break;
1698 // return true after calling the handler since we don't want
1699 // them to be passed onto the default handlers
1700 case QEvent::MouseButtonPress:
1701 if (d->mousePressEvent(event: static_cast<QMouseEvent *>(event)))
1702 return true;
1703 break;
1704 case QEvent::MouseButtonDblClick:
1705 if (d->mouseDoubleClickEvent(event: static_cast<QMouseEvent *>(event)))
1706 return true;
1707 break;
1708 case QEvent::MouseMove:
1709 if (d->mouseMoveEvent(event: static_cast<QMouseEvent *>(event)))
1710 return true;
1711 break;
1712 case QEvent::MouseButtonRelease:
1713 if (d->mouseReleaseEvent(event: static_cast<QMouseEvent *>(event)))
1714 return true;
1715 break;
1716 case QEvent::NonClientAreaMouseMove:
1717 case QEvent::NonClientAreaMouseButtonPress:
1718 case QEvent::NonClientAreaMouseButtonRelease:
1719 case QEvent::NonClientAreaMouseButtonDblClick:
1720 d->nonClientAreaMouseEvent(event: static_cast<QMouseEvent*>(event));
1721 return true;
1722 case QEvent::Move:
1723 d->moveEvent(event: static_cast<QMoveEvent*>(event));
1724 break;
1725 case QEvent::Resize:
1726 // if the mainwindow is plugging us, we don't want to update undocked geometry
1727 if (isFloating() && layout != nullptr && layout->pluggingWidget != this)
1728 d->undockedGeometry = geometry();
1729
1730 // Usually the window won't get resized while it's being moved, but it can happen,
1731 // for example on Windows when moving to a screen with bigger scale factor
1732 // If that happens we should update state->pressPos, otherwise it will be outside
1733 // the window when the window shrinks.
1734 if (d->state && d->state->dragging)
1735 d->recalculatePressPos(event: static_cast<QResizeEvent*>(event));
1736 break;
1737 default:
1738 break;
1739 }
1740 return QWidget::event(event);
1741}
1742
1743#ifndef QT_NO_ACTION
1744/*!
1745 Returns a checkable action that can be added to menus and toolbars so that
1746 the user can show or close this dock widget.
1747
1748 The action's text is set to the dock widget's window title.
1749
1750 The QAction object is owned by the QDockWidget. It will be automatically
1751 deleted when the QDockWidget is destroyed.
1752
1753 \note The action can not be used to programmatically show or hide the dock
1754 widget. Use the \l visible property for that.
1755
1756 \sa QAction::text, QWidget::windowTitle
1757 */
1758QAction * QDockWidget::toggleViewAction() const
1759{
1760 Q_D(const QDockWidget);
1761 return d->toggleViewAction;
1762}
1763#endif // QT_NO_ACTION
1764
1765/*!
1766 \fn void QDockWidget::featuresChanged(QDockWidget::DockWidgetFeatures features)
1767
1768 This signal is emitted when the \l features property changes. The
1769 \a features parameter gives the new value of the property.
1770*/
1771
1772/*!
1773 \fn void QDockWidget::topLevelChanged(bool topLevel)
1774
1775 This signal is emitted when the \l floating property changes.
1776 The \a topLevel parameter is true if the dock widget is now floating;
1777 otherwise it is false.
1778
1779 \sa isWindow()
1780*/
1781
1782/*!
1783 \fn void QDockWidget::allowedAreasChanged(Qt::DockWidgetAreas allowedAreas)
1784
1785 This signal is emitted when the \l allowedAreas property changes. The
1786 \a allowedAreas parameter gives the new value of the property.
1787*/
1788
1789/*!
1790 \fn void QDockWidget::visibilityChanged(bool visible)
1791 \since 4.3
1792
1793 This signal is emitted when the dock widget becomes \a visible (or
1794 invisible). This happens when the widget is hidden or shown, as
1795 well as when it is docked in a tabbed dock area and its tab
1796 becomes selected or unselected.
1797
1798 \note The signal can differ from QWidget::isVisible(). This can be the case, if
1799 a dock widget is minimized or tabified and associated to a non-selected or
1800 inactive tab.
1801*/
1802
1803/*!
1804 \fn void QDockWidget::dockLocationChanged(Qt::DockWidgetArea area)
1805 \since 4.3
1806
1807 This signal is emitted when the dock widget is moved to another
1808 dock \a area, or is moved to a different location in its current
1809 dock area. This happens when the dock widget is moved
1810 programmatically or is dragged to a new location by the user.
1811 \sa dockLocation(), setDockLocation()
1812*/
1813
1814/*!
1815 \since 4.3
1816
1817 Sets an arbitrary \a widget as the dock widget's title bar. If \a widget
1818 is \nullptr, any custom title bar widget previously set on the dock widget
1819 is removed, but not deleted, and the default title bar will be used
1820 instead.
1821
1822 If a title bar widget is set, QDockWidget will not use native window
1823 decorations when it is floated.
1824
1825 Here are some tips for implementing custom title bars:
1826
1827 \list
1828 \li Mouse events that are not explicitly handled by the title bar widget
1829 must be ignored by calling QMouseEvent::ignore(). These events then
1830 propagate to the QDockWidget parent, which handles them in the usual
1831 manner, moving when the title bar is dragged, docking and undocking
1832 when it is double-clicked, etc.
1833
1834 \li When DockWidgetVerticalTitleBar is set on QDockWidget, the title
1835 bar widget is repositioned accordingly. In resizeEvent(), the title
1836 bar should check what orientation it should assume:
1837 \snippet code/src_gui_widgets_qdockwidget.cpp 0
1838
1839 \li The title bar widget must have a valid QWidget::sizeHint() and
1840 QWidget::minimumSizeHint(). These functions should take into account
1841 the current orientation of the title bar.
1842
1843 \li It is not possible to remove a title bar from a dock widget. However,
1844 a similar effect can be achieved by setting a default constructed
1845 QWidget as the title bar widget.
1846 \endlist
1847
1848 Using qobject_cast() as shown above, the title bar widget has full access
1849 to its parent QDockWidget. Hence it can perform such operations as docking
1850 and hiding in response to user actions.
1851
1852 \sa titleBarWidget(), DockWidgetVerticalTitleBar
1853*/
1854
1855void QDockWidget::setTitleBarWidget(QWidget *widget)
1856{
1857 Q_D(QDockWidget);
1858 QDockWidgetLayout *layout
1859 = qobject_cast<QDockWidgetLayout*>(object: this->layout());
1860 layout->setWidgetForRole(r: QDockWidgetLayout::TitleBar, w: widget);
1861 d->updateButtons();
1862 if (isWindow()) {
1863 //this ensures the native decoration is drawn
1864 d->setWindowState(states: {QDockWidgetPrivate::WindowState::Floating,
1865 QDockWidgetPrivate::WindowState::Unplug});
1866 }
1867}
1868
1869/*!
1870 \since 6.9
1871
1872 Assigns this dock widget to \a area. If docked at another dock location, it
1873 will move to \a area. If floating or part of floating tabs, the next call
1874 of setFloating(false) will dock it at \a area.
1875
1876 \note setDockLocation(Qt::NoDockLocation) is equivalent to setFloating(true).
1877
1878 \sa dockLocation(), dockLocationChanged()
1879 */
1880void QDockWidget::setDockLocation(Qt::DockWidgetArea area)
1881{
1882 if (area == Qt::NoDockWidgetArea && !isFloating()) {
1883 setFloating(true);
1884 return;
1885 }
1886
1887 auto *mainWindow = const_cast<QMainWindow *>(mainwindow_from_dock(dock: this));
1888 Q_ASSERT(mainWindow);
1889 mainWindow->addDockWidget(area, dockwidget: this);
1890}
1891
1892/*!
1893 \property QDockWidget::dockLocation
1894 \since 6.9
1895
1896 \brief the current dock location, or Qt::NoDockLocation if this dock widget
1897 is floating or has no mainwindow parent.
1898 */
1899Qt::DockWidgetArea QDockWidget::dockLocation() const
1900{
1901 // QDockWidgetPrivate::setWindowState() emits NoDockWidgetArea if
1902 // the dock widget becomes floating.
1903 // QMainWindowLayout::dockWidgetArea() always returns the area where
1904 // the dock widget's item_list is kept.
1905 if (isFloating())
1906 return Qt::NoDockWidgetArea;
1907
1908 auto *mainWindow = mainwindow_from_dock(dock: this);
1909 Q_ASSERT(mainWindow);
1910 // FIXME in Qt 7: Make dockWidgetArea take a const QDockWidget* argument
1911 return mainWindow->dockWidgetArea(dockwidget: const_cast<QDockWidget *>(this));
1912}
1913
1914/*!
1915 \since 4.3
1916 Returns the custom title bar widget set on the QDockWidget, or
1917 \nullptr if no custom title bar has been set.
1918
1919 \sa setTitleBarWidget()
1920*/
1921
1922QWidget *QDockWidget::titleBarWidget() const
1923{
1924 QDockWidgetLayout *layout
1925 = qobject_cast<QDockWidgetLayout*>(object: this->layout());
1926 return layout->widgetForRole(r: QDockWidgetLayout::TitleBar);
1927}
1928
1929#ifndef QT_NO_DEBUG_STREAM
1930QDebug operator<<(QDebug dbg, const QDockWidget *dockWidget)
1931{
1932 QDebugStateSaver saver(dbg);
1933 dbg.nospace();
1934
1935 if (!dockWidget) {
1936 dbg << "QDockWidget(0x0)";
1937 return dbg;
1938 }
1939
1940 dbg << "QDockWidget(" << static_cast<const void *>(dockWidget);
1941 dbg << "->(ObjectName=" << dockWidget->objectName();
1942 dbg << "; floating=" << dockWidget->isFloating();
1943 dbg << "; features=" << dockWidget->features();
1944 dbg << ";))";
1945 return dbg;
1946}
1947#endif // QT_NO_DEBUG_STREAM
1948
1949QT_END_NAMESPACE
1950
1951#include "qdockwidget.moc"
1952#include "moc_qdockwidget.cpp"
1953#include "moc_qdockwidget_p.cpp"
1954

source code of qtbase/src/widgets/widgets/qdockwidget.cpp