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