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