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