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*>(object: 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(window: 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(e: 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(classname: "QStyleSheetStyle"))
172 effectiveStyle = static_cast<const QStyleSheetStyle *>(style)->baseStyle();
173#endif
174#if !defined(QT_NO_STYLE_PROXY)
175 if (style->inherits(classname: "QProxyStyle"))
176 effectiveStyle = static_cast<const QProxyStyle *>(style)->baseStyle();
177#endif
178
179 return effectiveStyle->inherits(classname: "QWindowsStyle");
180}
181
182QSize QDockWidgetTitleButton::dockButtonIconSize() const
183{
184 if (m_iconSize < 0) {
185 m_iconSize = style()->pixelMetric(metric: QStyle::PM_SmallIconSize, option: nullptr, widget: 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: style()))
191 m_iconSize = qMin(a: (10 * logicalDpiX()) / 96, b: 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(metric: QStyle::PM_DockWidgetTitleBarButtonMargin, option: nullptr, widget: this);
201 if (!icon().isNull()) {
202 const QSize sz = icon().actualSize(size: dockButtonIconSize());
203 size += qMax(a: sz.width(), b: 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(w: this);
227 opt.state |= QStyle::State_AutoRaise;
228
229 if (style()->styleHint(stylehint: QStyle::SH_DockWidget_ButtonsHaveFrame, opt: nullptr, widget: 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(pe: QStyle::PE_PanelButtonTool, opt: &opt, p: &p, w: 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(cc: QStyle::CC_ToolButton, opt: &opt, p: &p, widget: 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(c: 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 *>(object: 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(other: QLatin1String("xcb"), cs: 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(i: QDockWidgetLayout::TitleBar) == 0;
300}
301
302
303void QDockWidgetLayout::addItem(QLayoutItem*)
304{
305 qWarning(msg: "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 == nullptr)
315 continue;
316 if (index == cnt++)
317 return item;
318 }
319 return nullptr;
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 == 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
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(a: result.height(), b: minimumTitleWidth()));
355 result.setWidth(qMax(a: content.width(), b: 0));
356 } else {
357 result.setHeight(qMax(a: result.height(), b: 0));
358 result.setWidth(qMax(a: content.width(), b: minimumTitleWidth()));
359 }
360
361 QDockWidget *w = qobject_cast<QDockWidget*>(object: parentWidget());
362 const bool nativeDeco = nativeWindowDeco(floating);
363
364 int fw = floating && !nativeDeco
365 ? w->style()->pixelMetric(metric: QStyle::PM_DockWidgetFrameWidth, option: nullptr, widget: 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(a: result.height(), b: (int) QWIDGETSIZE_MAX));
377 result.setWidth(qMin(a: result.width(), b: (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(m: margins);
387 QSize max = w->maximumSize().shrunkBy(m: 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(otherSize: max).expandedTo(otherSize: min);
413}
414
415QSize QDockWidgetLayout::sizeHint() const
416{
417 QDockWidget *w = qobject_cast<QDockWidget*>(object: parentWidget());
418
419 QSize content(-1, -1);
420 if (item_list[Content] != 0)
421 content = item_list[Content]->sizeHint();
422
423 return sizeFromContent(content, floating: w->isFloating());
424}
425
426QSize QDockWidgetLayout::maximumSize() const
427{
428 if (item_list[Content] != 0) {
429 const QSize content = item_list[Content]->maximumSize();
430 return sizeFromContent(content, floating: parentWidget()->isWindow());
431 } else {
432 return parentWidget()->maximumSize();
433 }
434
435}
436
437QSize QDockWidgetLayout::minimumSize() const
438{
439 QDockWidget *w = qobject_cast<QDockWidget*>(object: parentWidget());
440
441 QSize content(0, 0);
442 if (item_list[Content] != 0)
443 content = item_list[Content]->minimumSize();
444
445 return sizeFromContent(content, floating: w->isFloating());
446}
447
448QWidget *QDockWidgetLayout::widgetForRole(Role r) const
449{
450 QLayoutItem *item = item_list.at(i: r);
451 return item == nullptr ? nullptr : item->widget();
452}
453
454QLayoutItem *QDockWidgetLayout::itemForRole(Role r) const
455{
456 return item_list.at(i: r);
457}
458
459void QDockWidgetLayout::setWidgetForRole(Role r, QWidget *w)
460{
461 QWidget *old = widgetForRole(r);
462 if (old != nullptr) {
463 old->hide();
464 removeWidget(w: 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
478static inline int pick(bool vertical, const QSize &size)
479{
480 return vertical ? size.height() : size.width();
481}
482
483static inline int perp(bool vertical, const QSize &size)
484{
485 return vertical ? size.width() : size.height();
486}
487
488int QDockWidgetLayout::minimumTitleWidth() const
489{
490 QDockWidget *q = qobject_cast<QDockWidget*>(object: parentWidget());
491
492 if (QWidget *title = widgetForRole(r: TitleBar))
493 return pick(vertical: verticalTitleBar, size: title->minimumSizeHint());
494
495 QSize closeSize(0, 0);
496 QSize floatSize(0, 0);
497 if (hasFeature(dockwidget: q, feature: QDockWidget::DockWidgetClosable)) {
498 if (QLayoutItem *item = item_list[CloseButton])
499 closeSize = item->widget()->sizeHint();
500 }
501 if (hasFeature(dockwidget: q, feature: 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(metric: QStyle::PM_DockWidgetTitleMargin, option: nullptr, widget: q);
509 int fw = q->style()->pixelMetric(metric: QStyle::PM_DockWidgetFrameWidth, option: nullptr, widget: q);
510
511 return pick(vertical: verticalTitleBar, size: closeSize)
512 + pick(vertical: verticalTitleBar, size: floatSize)
513 + titleHeight + 2*fw + 3*mw;
514}
515
516int QDockWidgetLayout::titleHeight() const
517{
518 QDockWidget *q = qobject_cast<QDockWidget*>(object: parentWidget());
519
520 if (QWidget *title = widgetForRole(r: TitleBar))
521 return perp(vertical: verticalTitleBar, size: 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(a: perp(vertical: verticalTitleBar, size: closeSize),
531 b: perp(vertical: verticalTitleBar, size: floatSize));
532
533 QFontMetrics titleFontMetrics = q->fontMetrics();
534 int mw = q->style()->pixelMetric(metric: QStyle::PM_DockWidgetTitleMargin, option: nullptr, widget: q);
535
536 return qMax(a: buttonHeight + 2, b: titleFontMetrics.height() + 2*mw);
537}
538
539void QDockWidgetLayout::setGeometry(const QRect &geometry)
540{
541 QDockWidget *q = qobject_cast<QDockWidget*>(object: parentWidget());
542
543 bool nativeDeco = nativeWindowDeco();
544
545 int fw = q->isFloating() && !nativeDeco
546 ? q->style()->pixelMetric(metric: QStyle::PM_DockWidgetFrameWidth, option: nullptr, widget: 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(option: &opt);
568
569 if (QLayoutItem *item = item_list[CloseButton]) {
570 if (!item->isEmpty()) {
571 QRect r = q->style()
572 ->subElementRect(subElement: QStyle::SE_DockWidgetCloseButton,
573 option: &opt, widget: 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(subElement: QStyle::SE_DockWidgetFloatButton,
583 option: &opt, widget: 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(dx1: 0, dy1: fw, dx2: -fw, dy2: -fw);
595 } else {
596 r.setTop(_titleArea.bottom() + 1);
597 r.adjust(dx1: fw, dy1: 0, dx2: -fw, dy2: -fw);
598 }
599 item->setGeometry(r);
600 }
601 }
602}
603
604void 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
617QDockWidgetItem::QDockWidgetItem(QDockWidget *dockWidget)
618 : QWidgetItem(dockWidget)
619{
620}
621
622QSize QDockWidgetItem::minimumSize() const
623{
624 QSize widgetMin(0, 0);
625 if (QLayoutItem *item = dockWidgetChildItem())
626 widgetMin = item->minimumSize();
627 return dockWidgetLayout()->sizeFromContent(content: widgetMin, floating: false);
628}
629
630QSize QDockWidgetItem::maximumSize() const
631{
632 if (QLayoutItem *item = dockWidgetChildItem()) {
633 return dockWidgetLayout()->sizeFromContent(content: item->maximumSize(), floating: false);
634 } else {
635 return QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
636 }
637}
638
639
640QSize QDockWidgetItem::sizeHint() const
641{
642 if (QLayoutItem *item = dockWidgetChildItem()) {
643 return dockWidgetLayout()->sizeFromContent(content: item->sizeHint(), floating: false);
644 } else {
645 return QWidgetItem::sizeHint();
646 }
647}
648
649/******************************************************************************
650** QDockWidgetPrivate
651*/
652
653void 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(sender: button, SIGNAL(clicked()), receiver: q, SLOT(_q_toggleTopLevel()));
663 layout->setWidgetForRole(r: QDockWidgetLayout::FloatButton, w: button);
664
665 button = new QDockWidgetTitleButton(q);
666 button->setObjectName(QLatin1String("qt_dockwidget_closebutton"));
667 QObject::connect(sender: button, SIGNAL(clicked()), receiver: q, SLOT(close()));
668 layout->setWidgetForRole(r: QDockWidgetLayout::CloseButton, w: button);
669
670 font = QApplication::font(className: "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(sender: toggleViewAction, SIGNAL(triggered(bool)),
679 receiver: 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*/
692void QDockWidget::initStyleOption(QStyleOptionDockWidget *option) const
693{
694 Q_D(const QDockWidget);
695
696 if (!option)
697 return;
698 QDockWidgetLayout *dwlayout = qobject_cast<QDockWidgetLayout*>(object: layout());
699
700 QDockWidgetGroupWindow *floatingTab = qobject_cast<QDockWidgetGroupWindow*>(object: 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(w: floatingTab && !isFloating() ? parentWidget() : this);
704 option->rect = dwlayout->titleArea();
705 option->title = d->fixedWindowTitle;
706 option->closable = hasFeature(dockwidget: this, feature: QDockWidget::DockWidgetClosable);
707 option->movable = hasFeature(dockwidget: this, feature: QDockWidget::DockWidgetMovable);
708 option->floatable = hasFeature(dockwidget: this, feature: QDockWidget::DockWidgetFloatable);
709
710 QDockWidgetLayout *l = qobject_cast<QDockWidgetLayout*>(object: layout());
711 option->verticalTitleBar = l->verticalTitleBar;
712}
713
714void 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
725void QDockWidgetPrivate::updateButtons()
726{
727 Q_Q(QDockWidget);
728 QDockWidgetLayout *dwLayout = qobject_cast<QDockWidgetLayout*>(object: layout);
729
730 QStyleOptionDockWidget opt;
731 q->initStyleOption(option: &opt);
732
733 bool customTitleBar = dwLayout->widgetForRole(r: QDockWidgetLayout::TitleBar) != nullptr;
734 bool nativeDeco = dwLayout->nativeWindowDeco();
735 bool hideButtons = nativeDeco || customTitleBar;
736
737 bool canClose = hasFeature(priv: this, feature: QDockWidget::DockWidgetClosable);
738 bool canFloat = hasFeature(priv: this, feature: QDockWidget::DockWidgetFloatable);
739
740 QAbstractButton *button
741 = qobject_cast<QAbstractButton*>(object: dwLayout->widgetForRole(r: QDockWidgetLayout::FloatButton));
742 button->setIcon(q->style()->standardIcon(standardIcon: QStyle::SP_TitleBarNormalButton, option: &opt, widget: 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(s: "Float"));
747 button->setAccessibleDescription(QDockWidget::tr(s: "Undocks and re-attaches the dock widget"));
748#endif
749 button
750 = qobject_cast <QAbstractButton*>(object: dwLayout->widgetForRole(r: QDockWidgetLayout::CloseButton));
751 button->setIcon(q->style()->standardIcon(standardIcon: QStyle::SP_TitleBarCloseButton, option: &opt, widget: 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(s: "Close"));
756 button->setAccessibleDescription(QDockWidget::tr(s: "Closes the dock widget"));
757#endif
758
759 layout->invalidate();
760}
761
762void 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 */
774void 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(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 */
801void 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(dock: q);
809 Q_ASSERT(layout != nullptr);
810
811 state->widgetItem = layout->unplug(widget: 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*>(object: 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 */
836void 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(dock: q);
845 Q_ASSERT(mainWindow != nullptr);
846 QMainWindowLayout *mwLayout = qt_mainwindow_layout(window: mainWindow);
847
848 if (abort || !mwLayout->plug(widgetItem: state->widgetItem)) {
849 if (hasFeature(priv: this, feature: 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*>(object: 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(area: mainWindow->dockWidgetArea(dockwidget: 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(widgetItem: state->widgetItem);
878 }
879 }
880 }
881 delete state;
882 state = nullptr;
883}
884
885void 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(ac: QWidgetResizeHandler::Resize, b: active);
894}
895
896bool QDockWidgetPrivate::isAnimating() const
897{
898 Q_Q(const QDockWidget);
899
900 QMainWindowLayout *mainWinLayout = qt_mainwindow_layout_from_dock(dock: q);
901 if (mainWinLayout == nullptr)
902 return false;
903
904 return (const void*)mainWinLayout->pluggingWidget == (const void*)q;
905}
906
907bool QDockWidgetPrivate::mousePressEvent(QMouseEvent *event)
908{
909#if QT_CONFIG(mainwindow)
910 Q_Q(QDockWidget);
911
912 QDockWidgetLayout *dwLayout
913 = qobject_cast<QDockWidgetLayout*>(object: layout);
914
915 if (!dwLayout->nativeWindowDeco()) {
916 QRect titleArea = dwLayout->titleArea();
917
918 QDockWidgetGroupWindow *floatingTab = qobject_cast<QDockWidgetGroupWindow*>(object: parent);
919
920 if (event->button() != Qt::LeftButton ||
921 !titleArea.contains(p: event->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(priv: this, feature: QDockWidget::DockWidgetMovable) && !q->isFloating()) ||
925 (qobject_cast<QMainWindow*>(object: parent) == 0 && !floatingTab) ||
926 isAnimating() || state != nullptr) {
927 return false;
928 }
929
930 initDrag(pos: event->pos(), nca: false);
931
932 if (state)
933 state->ctrlDrag = (hasFeature(priv: this, feature: QDockWidget::DockWidgetFloatable) && event->modifiers() & Qt::ControlModifier) ||
934 (!hasFeature(priv: this, feature: QDockWidget::DockWidgetMovable) && q->isFloating());
935
936 return true;
937 }
938
939#endif // QT_CONFIG(mainwindow)
940 return false;
941}
942
943bool QDockWidgetPrivate::mouseDoubleClickEvent(QMouseEvent *event)
944{
945 QDockWidgetLayout *dwLayout = qobject_cast<QDockWidgetLayout*>(object: layout);
946
947 if (!dwLayout->nativeWindowDeco()) {
948 QRect titleArea = dwLayout->titleArea();
949
950 if (event->button() == Qt::LeftButton && titleArea.contains(p: event->pos()) &&
951 hasFeature(priv: this, feature: QDockWidget::DockWidgetFloatable)) {
952 _q_toggleTopLevel();
953 return true;
954 }
955 }
956 return false;
957}
958
959bool 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 *>(object: layout);
970 QMainWindowLayout *mwlayout = qt_mainwindow_layout_from_dock(dock: q);
971 if (!dwlayout->nativeWindowDeco()) {
972 if (!state->dragging
973 && mwlayout->pluggingWidget == nullptr
974 && (event->pos() - state->pressPos).manhattanLength()
975 > QApplication::startDragDistance()) {
976
977#ifdef Q_OS_MACOS
978 if (windowHandle() && !q->isFloating()) {
979 // When using native widgets on mac, we have not yet been successful in
980 // starting a drag on an NSView that belongs to one window (QMainWindow),
981 // but continue the drag on another (QDockWidget). This is what happens if
982 // we try to make this widget floating during a drag. So as a fall back
983 // solution, we simply make this widget floating instead, when we would
984 // otherwise start a drag.
985 q->setFloating(true);
986 } else
987#endif
988 {
989 startDrag();
990 q->grabMouse();
991 ret = true;
992 }
993 }
994 }
995
996 if (state && state->dragging && !state->nca) {
997 QMargins windowMargins = q->window()->windowHandle()->frameMargins();
998 QPoint windowMarginOffset = QPoint(windowMargins.left(), windowMargins.top());
999 QPoint pos = event->globalPos() - state->pressPos - windowMarginOffset;
1000
1001 QDockWidgetGroupWindow *floatingTab = qobject_cast<QDockWidgetGroupWindow*>(object: parent);
1002 if (floatingTab && !q->isFloating())
1003 floatingTab->move(pos);
1004 else
1005 q->move(pos);
1006
1007 if (state && !state->ctrlDrag)
1008 mwlayout->hover(widgetItem: state->widgetItem, mousePos: event->globalPos());
1009
1010 ret = true;
1011 }
1012
1013#endif // QT_CONFIG(mainwindow)
1014 return ret;
1015}
1016
1017bool QDockWidgetPrivate::mouseReleaseEvent(QMouseEvent *event)
1018{
1019#if QT_CONFIG(mainwindow)
1020
1021 if (event->button() == Qt::LeftButton && state && !state->nca) {
1022 endDrag();
1023 return true; //filter out the event
1024 }
1025
1026#endif // QT_CONFIG(mainwindow)
1027 return false;
1028}
1029
1030void QDockWidgetPrivate::nonClientAreaMouseEvent(QMouseEvent *event)
1031{
1032 Q_Q(QDockWidget);
1033
1034 int fw = q->style()->pixelMetric(metric: QStyle::PM_DockWidgetFrameWidth, option: nullptr, widget: q);
1035
1036 QWidget *tl = q->topLevelWidget();
1037 QRect geo = tl->geometry();
1038 QRect titleRect = tl->frameGeometry();
1039 {
1040 titleRect.setLeft(geo.left());
1041 titleRect.setRight(geo.right());
1042 titleRect.setBottom(geo.top() - 1);
1043 titleRect.adjust(dx1: 0, dy1: fw, dx2: 0, dy2: 0);
1044 }
1045
1046 switch (event->type()) {
1047 case QEvent::NonClientAreaMouseButtonPress:
1048 if (!titleRect.contains(p: event->globalPos()))
1049 break;
1050 if (state != nullptr)
1051 break;
1052 if (qobject_cast<QMainWindow*>(object: parent) == 0 && qobject_cast<QDockWidgetGroupWindow*>(object: parent) == 0)
1053 break;
1054 if (isAnimating())
1055 break;
1056 initDrag(pos: event->pos(), nca: true);
1057 if (state == nullptr)
1058 break;
1059 state->ctrlDrag = (event->modifiers() & Qt::ControlModifier) ||
1060 (!hasFeature(priv: this, feature: QDockWidget::DockWidgetMovable) && q->isFloating());
1061 startDrag();
1062 break;
1063 case QEvent::NonClientAreaMouseMove:
1064 if (state == nullptr || !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 == nullptr || !state->dragging || !state->nca)
1101 return;
1102
1103 if (!q->isWindow() && qobject_cast<QDockWidgetGroupWindow*>(object: 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(dock: q);
1113 Q_ASSERT(layout != nullptr);
1114
1115 QPoint globalMousePos = event->pos() + state->pressPos;
1116 layout->hover(widgetItem: state->widgetItem, mousePos: globalMousePos);
1117}
1118
1119void QDockWidgetPrivate::unplug(const QRect &rect)
1120{
1121 Q_Q(QDockWidget);
1122 QRect r = rect;
1123 r.moveTopLeft(p: q->mapToGlobal(QPoint(0, 0)));
1124 QDockWidgetLayout *dwLayout = qobject_cast<QDockWidgetLayout*>(object: layout);
1125 if (dwLayout->nativeWindowDeco(floating: true))
1126 r.adjust(dx1: 0, dy1: dwLayout->titleHeight(), dx2: 0, dy2: 0);
1127 setWindowState(floating: true, unplug: true, rect: r);
1128}
1129
1130void QDockWidgetPrivate::plug(const QRect &rect)
1131{
1132 setWindowState(floating: false, unplug: 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(dock: q);
1141 if (mwlayout && mwlayout->dockWidgetArea(widget: q) == Qt::NoDockWidgetArea
1142 && !qobject_cast<QDockWidgetGroupWindow *>(object: 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*>(object: layout);
1157 const bool nativeDeco = dwLayout->nativeWindowDeco(floating);
1158
1159 if (nativeDeco) {
1160 flags |= Qt::CustomizeWindowHint | Qt::WindowTitleHint;
1161 if (hasFeature(priv: this, feature: 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(topLevel: floating);
1183 if (!floating && parent) {
1184 QMainWindowLayout *mwlayout = qt_mainwindow_layout_from_dock(dock: q);
1185 if (mwlayout)
1186 emit q->dockLocationChanged(area: mwlayout->dockWidgetArea(widget: q));
1187 } else {
1188 emit q->dockLocationChanged(area: 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 \note On macOS, if the QDockWidget has a native window handle (for example,
1240 if winId() is called on it or the child widget), then due to a limitation it will not be
1241 possible to drag the dock widget when undocking. Starting the drag will undock
1242 the dock widget, but a second drag will be needed to move the dock widget itself.
1243
1244 \sa QMainWindow, {Dock Widgets Example}
1245*/
1246
1247/*!
1248 \enum QDockWidget::DockWidgetFeature
1249
1250 \value DockWidgetClosable The dock widget can be closed. On some systems the dock
1251 widget always has a close button when it's floating
1252 (for example on MacOS 10.5).
1253 \value DockWidgetMovable The dock widget can be moved between docks
1254 by the user.
1255 \value DockWidgetFloatable The dock widget can be detached from the
1256 main window, and floated as an independent
1257 window.
1258 \value DockWidgetVerticalTitleBar The dock widget displays a vertical title
1259 bar on its left side. This can be used to
1260 increase the amount of vertical space in
1261 a QMainWindow.
1262 \value AllDockWidgetFeatures (Deprecated) The dock widget can be closed, moved,
1263 and floated. Since new features might be added in future
1264 releases, the look and behavior of dock widgets might
1265 change if you use this flag. Please specify individual
1266 flags instead.
1267 \value NoDockWidgetFeatures The dock widget cannot be closed, moved,
1268 or floated.
1269
1270 \omitvalue DockWidgetFeatureMask
1271 \omitvalue Reserved
1272*/
1273
1274/*!
1275 \property QDockWidget::windowTitle
1276 \brief the dock widget title (caption)
1277
1278 By default, this property contains an empty string.
1279*/
1280
1281/*!
1282 Constructs a QDockWidget with parent \a parent and window flags \a
1283 flags. The dock widget will be placed in the left dock widget
1284 area.
1285*/
1286QDockWidget::QDockWidget(QWidget *parent, Qt::WindowFlags flags)
1287 : QWidget(*new QDockWidgetPrivate, parent, flags)
1288{
1289 Q_D(QDockWidget);
1290 d->init();
1291}
1292
1293/*!
1294 Constructs a QDockWidget with parent \a parent and window flags \a
1295 flags. The dock widget will be placed in the left dock widget
1296 area.
1297
1298 The window title is set to \a title. This title is used when the
1299 QDockWidget is docked and undocked. It is also used in the context
1300 menu provided by QMainWindow.
1301
1302 \sa setWindowTitle()
1303*/
1304QDockWidget::QDockWidget(const QString &title, QWidget *parent, Qt::WindowFlags flags)
1305 : QDockWidget(parent, flags)
1306{
1307 setWindowTitle(title);
1308}
1309
1310/*!
1311 Destroys the dock widget.
1312*/
1313QDockWidget::~QDockWidget()
1314{ }
1315
1316/*!
1317 Returns the widget for the dock widget. This function returns zero
1318 if the widget has not been set.
1319
1320 \sa setWidget()
1321*/
1322QWidget *QDockWidget::widget() const
1323{
1324 QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(object: this->layout());
1325 return layout->widgetForRole(r: QDockWidgetLayout::Content);
1326}
1327
1328/*!
1329 Sets the widget for the dock widget to \a widget.
1330
1331 If the dock widget is visible when \a widget is added, you must
1332 \l{QWidget::}{show()} it explicitly.
1333
1334 Note that you must add the layout of the \a widget before you call
1335 this function; if not, the \a widget will not be visible.
1336
1337 \sa widget()
1338*/
1339void QDockWidget::setWidget(QWidget *widget)
1340{
1341 QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(object: this->layout());
1342 layout->setWidgetForRole(r: QDockWidgetLayout::Content, w: widget);
1343}
1344
1345/*!
1346 \property QDockWidget::features
1347 \brief whether the dock widget is movable, closable, and floatable
1348
1349 By default, this property is set to a combination of DockWidgetClosable,
1350 DockWidgetMovable and DockWidgetFloatable.
1351
1352 \sa DockWidgetFeature
1353*/
1354
1355void QDockWidget::setFeatures(QDockWidget::DockWidgetFeatures features)
1356{
1357 Q_D(QDockWidget);
1358 features &= DockWidgetFeatureMask;
1359 if (d->features == features)
1360 return;
1361 const bool closableChanged = (d->features ^ features) & DockWidgetClosable;
1362 d->features = features;
1363 QDockWidgetLayout *layout
1364 = qobject_cast<QDockWidgetLayout*>(object: this->layout());
1365 layout->setVerticalTitleBar(features & DockWidgetVerticalTitleBar);
1366 d->updateButtons();
1367 d->toggleViewAction->setEnabled((d->features & DockWidgetClosable) == DockWidgetClosable);
1368 emit featuresChanged(features: d->features);
1369 update();
1370 if (closableChanged && layout->nativeWindowDeco()) {
1371 QDockWidgetGroupWindow *floatingTab = qobject_cast<QDockWidgetGroupWindow *>(object: parent());
1372 if (floatingTab && !isFloating())
1373 floatingTab->adjustFlags();
1374 else
1375 d->setWindowState(floating: true /*floating*/, unplug: true /*unplug*/); //this ensures the native decoration is drawn
1376 }
1377}
1378
1379QDockWidget::DockWidgetFeatures QDockWidget::features() const
1380{
1381 Q_D(const QDockWidget);
1382 return d->features;
1383}
1384
1385/*!
1386 \property QDockWidget::floating
1387 \brief whether the dock widget is floating
1388
1389 A floating dock widget is presented to the user as an independent
1390 window "on top" of its parent QMainWindow, instead of being
1391 docked in the QMainWindow.
1392
1393 By default, this property is \c true.
1394
1395 When this property changes, the \c {topLevelChanged()} signal is emitted.
1396
1397 \sa isWindow(), topLevelChanged()
1398*/
1399void QDockWidget::setFloating(bool floating)
1400{
1401 Q_D(QDockWidget);
1402
1403 // the initial click of a double-click may have started a drag...
1404 if (d->state != nullptr)
1405 d->endDrag(abort: true);
1406
1407 QRect r = d->undockedGeometry;
1408 // Keep position when undocking for the first time.
1409 if (floating && isVisible() && !r.isValid())
1410 r = QRect(mapToGlobal(QPoint(0, 0)), size());
1411
1412 d->setWindowState(floating, unplug: false, rect: floating ? r : QRect());
1413
1414 if (floating && r.isNull()) {
1415 if (x() < 0 || y() < 0) //may happen if we have been hidden
1416 move(QPoint());
1417 setAttribute(Qt::WA_Moved, on: false); //we want it at the default position
1418 }
1419}
1420
1421/*!
1422 \property QDockWidget::allowedAreas
1423 \brief areas where the dock widget may be placed
1424
1425 The default is Qt::AllDockWidgetAreas.
1426
1427 \sa Qt::DockWidgetArea
1428*/
1429
1430void QDockWidget::setAllowedAreas(Qt::DockWidgetAreas areas)
1431{
1432 Q_D(QDockWidget);
1433 areas &= Qt::DockWidgetArea_Mask;
1434 if (areas == d->allowedAreas)
1435 return;
1436 d->allowedAreas = areas;
1437 emit allowedAreasChanged(allowedAreas: d->allowedAreas);
1438}
1439
1440Qt::DockWidgetAreas QDockWidget::allowedAreas() const
1441{
1442 Q_D(const QDockWidget);
1443 return d->allowedAreas;
1444}
1445
1446/*!
1447 \fn bool QDockWidget::isAreaAllowed(Qt::DockWidgetArea area) const
1448
1449 Returns \c true if this dock widget can be placed in the given \a area;
1450 otherwise returns \c false.
1451*/
1452
1453/*! \reimp */
1454void QDockWidget::changeEvent(QEvent *event)
1455{
1456 Q_D(QDockWidget);
1457 QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(object: this->layout());
1458
1459 switch (event->type()) {
1460 case QEvent::ModifiedChange:
1461 case QEvent::WindowTitleChange:
1462 update(layout->titleArea());
1463#ifndef QT_NO_ACTION
1464 d->fixedWindowTitle = qt_setWindowTitle_helperHelper(windowTitle(), this);
1465 d->toggleViewAction->setText(d->fixedWindowTitle);
1466#endif
1467#if QT_CONFIG(tabbar)
1468 {
1469 if (QMainWindowLayout *winLayout = qt_mainwindow_layout_from_dock(dock: this)) {
1470 if (QDockAreaLayoutInfo *info = winLayout->layoutState.dockAreaLayout.info(widget: this))
1471 info->updateTabBar();
1472 }
1473 }
1474#endif // QT_CONFIG(tabbar)
1475 break;
1476 default:
1477 break;
1478 }
1479 QWidget::changeEvent(event);
1480}
1481
1482/*! \reimp */
1483void QDockWidget::closeEvent(QCloseEvent *event)
1484{
1485 Q_D(QDockWidget);
1486 if (d->state)
1487 d->endDrag(abort: true);
1488 QWidget::closeEvent(event);
1489}
1490
1491/*! \reimp */
1492void QDockWidget::paintEvent(QPaintEvent *event)
1493{
1494 Q_UNUSED(event)
1495 Q_D(QDockWidget);
1496
1497 QDockWidgetLayout *layout
1498 = qobject_cast<QDockWidgetLayout*>(object: this->layout());
1499 bool customTitleBar = layout->widgetForRole(r: QDockWidgetLayout::TitleBar) != nullptr;
1500 bool nativeDeco = layout->nativeWindowDeco();
1501
1502 if (!nativeDeco && !customTitleBar) {
1503 QStylePainter p(this);
1504 // ### Add PixelMetric to change spacers, so style may show border
1505 // when not floating.
1506 if (isFloating()) {
1507 QStyleOptionFrame framOpt;
1508 framOpt.init(w: this);
1509 p.drawPrimitive(pe: QStyle::PE_FrameDockWidget, opt: framOpt);
1510 }
1511
1512 // Title must be painted after the frame, since the areas overlap, and
1513 // the title may wish to extend out to all sides (eg. Vista style)
1514 QStyleOptionDockWidget titleOpt;
1515 initStyleOption(option: &titleOpt);
1516 if (font() == QApplication::font(className: "QDockWidget")) {
1517 titleOpt.fontMetrics = QFontMetrics(d->font);
1518 p.setFont(d->font);
1519 }
1520
1521 p.drawControl(ce: QStyle::CE_DockWidgetTitle, opt: titleOpt);
1522 }
1523}
1524
1525/*! \reimp */
1526bool QDockWidget::event(QEvent *event)
1527{
1528 Q_D(QDockWidget);
1529
1530 QMainWindow *win = qobject_cast<QMainWindow*>(object: parentWidget());
1531 QMainWindowLayout *layout = qt_mainwindow_layout_from_dock(dock: this);
1532
1533 switch (event->type()) {
1534#ifndef QT_NO_ACTION
1535 case QEvent::Hide:
1536 if (layout != nullptr)
1537 layout->keepSize(w: this);
1538 d->toggleViewAction->setChecked(false);
1539 emit visibilityChanged(visible: false);
1540 break;
1541 case QEvent::Show: {
1542 d->toggleViewAction->setChecked(true);
1543 QPoint parentTopLeft(0, 0);
1544 if (isWindow()) {
1545 const QScreen *screen = d->associatedScreen();
1546 parentTopLeft = screen
1547 ? screen->availableVirtualGeometry().topLeft()
1548 : QGuiApplication::primaryScreen()->availableVirtualGeometry().topLeft();
1549 }
1550 emit visibilityChanged(visible: geometry().right() >= parentTopLeft.x() && geometry().bottom() >= parentTopLeft.y());
1551}
1552 break;
1553#endif
1554 case QEvent::ApplicationLayoutDirectionChange:
1555 case QEvent::LayoutDirectionChange:
1556 case QEvent::StyleChange:
1557 case QEvent::ParentChange:
1558 d->updateButtons();
1559 break;
1560 case QEvent::ZOrderChange: {
1561 bool onTop = false;
1562 if (win != nullptr) {
1563 const QObjectList &siblings = win->children();
1564 onTop = siblings.count() > 0 && siblings.last() == (QObject*)this;
1565 }
1566#if QT_CONFIG(tabbar)
1567 if (!isFloating() && layout != nullptr && onTop)
1568 layout->raise(widget: this);
1569#endif
1570 break;
1571 }
1572 case QEvent::WindowActivate:
1573 case QEvent::WindowDeactivate:
1574 update(qobject_cast<QDockWidgetLayout *>(object: this->layout())->titleArea());
1575 break;
1576 case QEvent::ContextMenu:
1577 if (d->state) {
1578 event->accept();
1579 return true;
1580 }
1581 break;
1582 // return true after calling the handler since we don't want
1583 // them to be passed onto the default handlers
1584 case QEvent::MouseButtonPress:
1585 if (d->mousePressEvent(event: static_cast<QMouseEvent *>(event)))
1586 return true;
1587 break;
1588 case QEvent::MouseButtonDblClick:
1589 if (d->mouseDoubleClickEvent(event: static_cast<QMouseEvent *>(event)))
1590 return true;
1591 break;
1592 case QEvent::MouseMove:
1593 if (d->mouseMoveEvent(event: static_cast<QMouseEvent *>(event)))
1594 return true;
1595 break;
1596 case QEvent::MouseButtonRelease:
1597 if (d->mouseReleaseEvent(event: static_cast<QMouseEvent *>(event)))
1598 return true;
1599 break;
1600 case QEvent::NonClientAreaMouseMove:
1601 case QEvent::NonClientAreaMouseButtonPress:
1602 case QEvent::NonClientAreaMouseButtonRelease:
1603 case QEvent::NonClientAreaMouseButtonDblClick:
1604 d->nonClientAreaMouseEvent(event: static_cast<QMouseEvent*>(event));
1605 return true;
1606 case QEvent::Move:
1607 d->moveEvent(event: static_cast<QMoveEvent*>(event));
1608 break;
1609 case QEvent::Resize:
1610 // if the mainwindow is plugging us, we don't want to update undocked geometry
1611 if (isFloating() && layout != nullptr && layout->pluggingWidget != this)
1612 d->undockedGeometry = geometry();
1613
1614 // Usually the window won't get resized while it's being moved, but it can happen,
1615 // for example on Windows when moving to a screen with bigger scale factor
1616 // (and Qt::AA_EnableHighDpiScaling is enabled). If that happens we should
1617 // update state->pressPos, otherwise it will be outside the window when the window shrinks.
1618 if (d->state && d->state->dragging)
1619 d->recalculatePressPos(event: static_cast<QResizeEvent*>(event));
1620 break;
1621 default:
1622 break;
1623 }
1624 return QWidget::event(event);
1625}
1626
1627#ifndef QT_NO_ACTION
1628/*!
1629 Returns a checkable action that can be added to menus and toolbars so that
1630 the user can show or close this dock widget.
1631
1632 The action's text is set to the dock widget's window title.
1633
1634 \note The action can not be used to programmatically show or hide the dock
1635 widget. Use the \l visible property for that.
1636
1637 \sa QAction::text, QWidget::windowTitle
1638 */
1639QAction * QDockWidget::toggleViewAction() const
1640{
1641 Q_D(const QDockWidget);
1642 return d->toggleViewAction;
1643}
1644#endif // QT_NO_ACTION
1645
1646/*!
1647 \fn void QDockWidget::featuresChanged(QDockWidget::DockWidgetFeatures features)
1648
1649 This signal is emitted when the \l features property changes. The
1650 \a features parameter gives the new value of the property.
1651*/
1652
1653/*!
1654 \fn void QDockWidget::topLevelChanged(bool topLevel)
1655
1656 This signal is emitted when the \l floating property changes.
1657 The \a topLevel parameter is true if the dock widget is now floating;
1658 otherwise it is false.
1659
1660 \sa isWindow()
1661*/
1662
1663/*!
1664 \fn void QDockWidget::allowedAreasChanged(Qt::DockWidgetAreas allowedAreas)
1665
1666 This signal is emitted when the \l allowedAreas property changes. The
1667 \a allowedAreas parameter gives the new value of the property.
1668*/
1669
1670/*!
1671 \fn void QDockWidget::visibilityChanged(bool visible)
1672 \since 4.3
1673
1674 This signal is emitted when the dock widget becomes \a visible (or
1675 invisible). This happens when the widget is hidden or shown, as
1676 well as when it is docked in a tabbed dock area and its tab
1677 becomes selected or unselected.
1678*/
1679
1680/*!
1681 \fn void QDockWidget::dockLocationChanged(Qt::DockWidgetArea area)
1682 \since 4.3
1683
1684 This signal is emitted when the dock widget is moved to another
1685 dock \a area, or is moved to a different location in its current
1686 dock area. This happens when the dock widget is moved
1687 programmatically or is dragged to a new location by the user.
1688*/
1689
1690/*!
1691 \since 4.3
1692
1693 Sets an arbitrary \a widget as the dock widget's title bar. If \a widget
1694 is \nullptr, any custom title bar widget previously set on the dock widget
1695 is removed, but not deleted, and the default title bar will be used
1696 instead.
1697
1698 If a title bar widget is set, QDockWidget will not use native window
1699 decorations when it is floated.
1700
1701 Here are some tips for implementing custom title bars:
1702
1703 \list
1704 \li Mouse events that are not explicitly handled by the title bar widget
1705 must be ignored by calling QMouseEvent::ignore(). These events then
1706 propagate to the QDockWidget parent, which handles them in the usual
1707 manner, moving when the title bar is dragged, docking and undocking
1708 when it is double-clicked, etc.
1709
1710 \li When DockWidgetVerticalTitleBar is set on QDockWidget, the title
1711 bar widget is repositioned accordingly. In resizeEvent(), the title
1712 bar should check what orientation it should assume:
1713 \snippet code/src_gui_widgets_qdockwidget.cpp 0
1714
1715 \li The title bar widget must have a valid QWidget::sizeHint() and
1716 QWidget::minimumSizeHint(). These functions should take into account
1717 the current orientation of the title bar.
1718
1719 \li It is not possible to remove a title bar from a dock widget. However,
1720 a similar effect can be achieved by setting a default constructed
1721 QWidget as the title bar widget.
1722 \endlist
1723
1724 Using qobject_cast() as shown above, the title bar widget has full access
1725 to its parent QDockWidget. Hence it can perform such operations as docking
1726 and hiding in response to user actions.
1727
1728 \sa titleBarWidget(), DockWidgetVerticalTitleBar
1729*/
1730
1731void QDockWidget::setTitleBarWidget(QWidget *widget)
1732{
1733 Q_D(QDockWidget);
1734 QDockWidgetLayout *layout
1735 = qobject_cast<QDockWidgetLayout*>(object: this->layout());
1736 layout->setWidgetForRole(r: QDockWidgetLayout::TitleBar, w: widget);
1737 d->updateButtons();
1738 if (isWindow()) {
1739 //this ensures the native decoration is drawn
1740 d->setWindowState(floating: true /*floating*/, unplug: true /*unplug*/);
1741 }
1742}
1743
1744/*!
1745 \since 4.3
1746 Returns the custom title bar widget set on the QDockWidget, or
1747 \nullptr if no custom title bar has been set.
1748
1749 \sa setTitleBarWidget()
1750*/
1751
1752QWidget *QDockWidget::titleBarWidget() const
1753{
1754 QDockWidgetLayout *layout
1755 = qobject_cast<QDockWidgetLayout*>(object: this->layout());
1756 return layout->widgetForRole(r: QDockWidgetLayout::TitleBar);
1757}
1758
1759QT_END_NAMESPACE
1760
1761#include "qdockwidget.moc"
1762#include "moc_qdockwidget.cpp"
1763#include "moc_qdockwidget_p.cpp"
1764

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