1
/****************************************************************************
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4
** Contact: http://www.qt-project.org/legal
6
** This file is part of the QtGui module of the Qt Toolkit.
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 Digia. For licensing terms and
14
** conditions see http://qt.digia.com/licensing. For further information
15
** use the contact form at http://qt.digia.com/contact-us.
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 2.1 as published by the Free Software
20
** Foundation and appearing in the file LICENSE.LGPL included in the
21
** packaging of this file. Please review the following information to
22
** ensure the GNU Lesser General Public License version 2.1 requirements
23
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25
** In addition, as a special exception, Digia gives you certain additional
26
** rights. These rights are described in the Digia Qt LGPL Exception
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29
** GNU General Public License Usage
30
** Alternatively, this file may be used under the terms of the GNU
31
** General Public License version 3.0 as published by the Free Software
32
** Foundation and appearing in the file LICENSE.GPL included in the
33
** packaging of this file. Please review the following information to
34
** ensure the GNU General Public License version 3.0 requirements will be
35
** met: http://www.gnu.org/copyleft/gpl.html.
40
****************************************************************************/
42
#include "qdockwidget.h"
44
#ifndef QT_NO_DOCKWIDGET
46
#include <qapplication.h>
47
#include <qdesktopwidget.h>
48
#include <qdrawutil.h>
50
#include <qfontmetrics.h>
52
#include <qmainwindow.h>
53
#include <qrubberband.h>
54
#include <qstylepainter.h>
55
#include <qtoolbutton.h>
58
#include <qpa/qplatformwindow.h>
59
#include <private/qwidgetresizehandler_p.h>
61
#include "qdockwidget_p.h"
62
#include "qmainwindowlayout_p.h"
64
#include <private/qapplication_p.h>
65
#include <private/qt_mac_p.h>
66
#include <private/qmacstyle_mac_p.h>
71
extern QString qt_setWindowTitle_helperHelper(const QString&, const QWidget*); // qwidget.cpp
74
extern QMainWindowLayout *qt_mainwindow_layout(const QMainWindow *window);
76
static inline bool hasFeature(const QDockWidgetPrivate *priv, QDockWidget::DockWidgetFeature feature)
77
{ return (priv->features & feature) == feature; }
79
static inline bool hasFeature(const QDockWidget *dockwidget, QDockWidget::DockWidgetFeature feature)
80
{ return (dockwidget->features() & feature) == feature; }
86
[+] is the float button
87
[X] is the close button
89
+-------------------------------+
90
| Dock Window Title [+][X]|
91
+-------------------------------+
93
| place to put the single |
94
| QDockWidget child (this space |
95
| does not yet have a name) |
105
+-------------------------------+
109
/******************************************************************************
110
** QDockWidgetTitleButton
113
class QDockWidgetTitleButton : public QAbstractButton
118
QDockWidgetTitleButton(QDockWidget *dockWidget);
120
QSize sizeHint() const;
121
inline QSize minimumSizeHint() const
122
{ return sizeHint(); }
124
void enterEvent(QEvent *event);
125
void leaveEvent(QEvent *event);
126
void paintEvent(QPaintEvent *event);
130
QDockWidgetTitleButton::QDockWidgetTitleButton(QDockWidget *dockWidget)
131
: QAbstractButton(dockWidget)
133
setFocusPolicy(Qt::NoFocus);
136
QSize QDockWidgetTitleButton::sizeHint() const
140
int size = 2*style()->pixelMetric(QStyle::PM_DockWidgetTitleBarButtonMargin, 0, this);
141
if (!icon().isNull()) {
142
int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this);
143
QSize sz = icon().actualSize(QSize(iconSize, iconSize));
144
size += qMax(sz.width(), sz.height());
147
return QSize(size, size);
150
void QDockWidgetTitleButton::enterEvent(QEvent *event)
152
if (isEnabled()) update();
153
QAbstractButton::enterEvent(event);
156
void QDockWidgetTitleButton::leaveEvent(QEvent *event)
158
if (isEnabled()) update();
159
QAbstractButton::leaveEvent(event);
162
void QDockWidgetTitleButton::paintEvent(QPaintEvent *)
166
QStyleOptionToolButton opt;
168
opt.state |= QStyle::State_AutoRaise;
170
if (style()->styleHint(QStyle::SH_DockWidget_ButtonsHaveFrame, 0, this))
172
if (isEnabled() && underMouse() && !isChecked() && !isDown())
173
opt.state |= QStyle::State_Raised;
175
opt.state |= QStyle::State_On;
177
opt.state |= QStyle::State_Sunken;
178
style()->drawPrimitive(QStyle::PE_PanelButtonTool, &opt, &p, this);
183
opt.activeSubControls = 0;
184
opt.features = QStyleOptionToolButton::None;
185
opt.arrowType = Qt::NoArrow;
186
int size = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this);
187
opt.iconSize = QSize(size, size);
188
style()->drawComplexControl(QStyle::CC_ToolButton, &opt, &p, this);
191
/******************************************************************************
195
QDockWidgetLayout::QDockWidgetLayout(QWidget *parent)
196
: QLayout(parent), verticalTitleBar(false), item_list(RoleCount, 0)
200
QDockWidgetLayout::~QDockWidgetLayout()
202
qDeleteAll(item_list);
205
bool QDockWidgetLayout::nativeWindowDeco() const
207
return nativeWindowDeco(parentWidget()->isWindow());
212
static const bool xcb = !QGuiApplication::platformName().compare(QLatin1String("xcb"), Qt::CaseInsensitive);
216
bool QDockWidgetLayout::nativeWindowDeco(bool floating) const
221
return !isXcb() && (floating && item_list[QDockWidgetLayout::TitleBar] == 0);
226
void QDockWidgetLayout::addItem(QLayoutItem*)
228
qWarning() << "QDockWidgetLayout::addItem(): please use QDockWidgetLayout::setWidget()";
232
QLayoutItem *QDockWidgetLayout::itemAt(int index) const
235
for (int i = 0; i < item_list.count(); ++i) {
236
QLayoutItem *item = item_list.at(i);
245
QLayoutItem *QDockWidgetLayout::takeAt(int index)
248
for (int i = 0; i < item_list.count(); ++i) {
249
QLayoutItem *item = item_list.at(i);
262
int QDockWidgetLayout::count() const
265
for (int i = 0; i < item_list.count(); ++i) {
272
QSize QDockWidgetLayout::sizeFromContent(const QSize &content, bool floating) const
274
QSize result = content;
276
if (verticalTitleBar) {
277
result.setHeight(qMax(result.height(), minimumTitleWidth()));
278
result.setWidth(qMax(content.width(), 0));
280
result.setHeight(qMax(result.height(), 0));
281
result.setWidth(qMax(content.width(), minimumTitleWidth()));
284
QDockWidget *w = qobject_cast<QDockWidget*>(parentWidget());
285
const bool nativeDeco = nativeWindowDeco(floating);
287
int fw = floating && !nativeDeco
288
? w->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, w)
291
const int th = titleHeight();
293
if (verticalTitleBar)
294
result += QSize(th + 2*fw, 2*fw);
296
result += QSize(2*fw, th + 2*fw);
299
result.setHeight(qMin(result.height(), (int) QWIDGETSIZE_MAX));
300
result.setWidth(qMin(result.width(), (int) QWIDGETSIZE_MAX));
302
if (content.width() < 0)
304
if (content.height() < 0)
305
result.setHeight(-1);
307
int left, top, right, bottom;
308
w->getContentsMargins(&left, &top, &right, &bottom);
309
//we need to subtract the contents margin (it will be added by the caller)
310
QSize min = w->minimumSize() - QSize(left + right, top + bottom);
311
QSize max = w->maximumSize() - QSize(left + right, top + bottom);
313
/* A floating dockwidget will automatically get its minimumSize set to the layout's
314
minimum size + deco. We're *not* interested in this, we only take minimumSize()
315
into account if the user set it herself. Otherwise we end up expanding the result
316
of a calculation for a non-floating dock widget to a floating dock widget's
317
minimum size + window decorations. */
319
uint explicitMin = 0;
320
uint explicitMax = 0;
321
if (w->d_func()->extra != 0) {
322
explicitMin = w->d_func()->extra->explicitMinSize;
323
explicitMax = w->d_func()->extra->explicitMaxSize;
326
if (!(explicitMin & Qt::Horizontal) || min.width() == 0)
328
if (!(explicitMin & Qt::Vertical) || min.height() == 0)
331
if (!(explicitMax & Qt::Horizontal))
332
max.setWidth(QWIDGETSIZE_MAX);
333
if (!(explicitMax & Qt::Vertical))
334
max.setHeight(QWIDGETSIZE_MAX);
336
return result.boundedTo(max).expandedTo(min);
339
QSize QDockWidgetLayout::sizeHint() const
341
QDockWidget *w = qobject_cast<QDockWidget*>(parentWidget());
343
QSize content(-1, -1);
344
if (item_list[Content] != 0)
345
content = item_list[Content]->sizeHint();
347
return sizeFromContent(content, w->isFloating());
350
QSize QDockWidgetLayout::maximumSize() const
352
if (item_list[Content] != 0) {
353
const QSize content = item_list[Content]->maximumSize();
354
return sizeFromContent(content, parentWidget()->isWindow());
356
return parentWidget()->maximumSize();
361
QSize QDockWidgetLayout::minimumSize() const
363
QDockWidget *w = qobject_cast<QDockWidget*>(parentWidget());
366
if (item_list[Content] != 0)
367
content = item_list[Content]->minimumSize();
369
return sizeFromContent(content, w->isFloating());
372
QWidget *QDockWidgetLayout::widgetForRole(Role r) const
374
QLayoutItem *item = item_list.at(r);
375
return item == 0 ? 0 : item->widget();
378
QLayoutItem *QDockWidgetLayout::itemForRole(Role r) const
380
return item_list.at(r);
383
void QDockWidgetLayout::setWidgetForRole(Role r, QWidget *w)
385
QWidget *old = widgetForRole(r);
393
item_list[r] = new QWidgetItemV2(w);
402
static inline int pick(bool vertical, const QSize &size)
404
return vertical ? size.height() : size.width();
407
static inline int perp(bool vertical, const QSize &size)
409
return vertical ? size.width() : size.height();
412
int QDockWidgetLayout::minimumTitleWidth() const
414
QDockWidget *q = qobject_cast<QDockWidget*>(parentWidget());
416
if (QWidget *title = widgetForRole(TitleBar))
417
return pick(verticalTitleBar, title->minimumSizeHint());
419
QSize closeSize(0, 0);
420
QSize floatSize(0, 0);
421
if (hasFeature(q, QDockWidget::DockWidgetClosable)) {
422
if (QLayoutItem *item = item_list[CloseButton])
423
closeSize = item->widget()->sizeHint();
425
if (hasFeature(q, QDockWidget::DockWidgetFloatable)) {
426
if (QLayoutItem *item = item_list[FloatButton])
427
floatSize = item->widget()->sizeHint();
430
int titleHeight = this->titleHeight();
432
int mw = q->style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, 0, q);
433
int fw = q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, q);
435
return pick(verticalTitleBar, closeSize)
436
+ pick(verticalTitleBar, floatSize)
437
+ titleHeight + 2*fw + 3*mw;
440
int QDockWidgetLayout::titleHeight() const
442
QDockWidget *q = qobject_cast<QDockWidget*>(parentWidget());
444
if (QWidget *title = widgetForRole(TitleBar))
445
return perp(verticalTitleBar, title->sizeHint());
447
QSize closeSize(0, 0);
448
QSize floatSize(0, 0);
449
if (QLayoutItem *item = item_list[CloseButton])
450
closeSize = item->widget()->sizeHint();
451
if (QLayoutItem *item = item_list[FloatButton])
452
floatSize = item->widget()->sizeHint();
454
int buttonHeight = qMax(perp(verticalTitleBar, closeSize),
455
perp(verticalTitleBar, floatSize));
457
QFontMetrics titleFontMetrics = q->fontMetrics();
459
if (qobject_cast<QMacStyle *>(q->style())) {
460
//### this breaks on proxy styles. (But is this code still called?)
461
QFont font = qt_app_fonts_hash()->value("QToolButton", q->font());
462
titleFontMetrics = QFontMetrics(font);
466
int mw = q->style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, 0, q);
468
return qMax(buttonHeight + 2, titleFontMetrics.height() + 2*mw);
471
void QDockWidgetLayout::setGeometry(const QRect &geometry)
473
QDockWidget *q = qobject_cast<QDockWidget*>(parentWidget());
475
bool nativeDeco = nativeWindowDeco();
477
int fw = q->isFloating() && !nativeDeco
478
? q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, q)
482
if (QLayoutItem *item = item_list[Content])
483
item->setGeometry(geometry);
485
int titleHeight = this->titleHeight();
487
if (verticalTitleBar) {
488
_titleArea = QRect(QPoint(fw, fw),
489
QSize(titleHeight, geometry.height() - (fw * 2)));
491
_titleArea = QRect(QPoint(fw, fw),
492
QSize(geometry.width() - (fw * 2), titleHeight));
495
if (QLayoutItem *item = item_list[TitleBar]) {
496
item->setGeometry(_titleArea);
498
QStyleOptionDockWidgetV2 opt;
499
q->initStyleOption(&opt);
501
if (QLayoutItem *item = item_list[CloseButton]) {
502
if (!item->isEmpty()) {
504
->subElementRect(QStyle::SE_DockWidgetCloseButton,
507
item->setGeometry(r);
511
if (QLayoutItem *item = item_list[FloatButton]) {
512
if (!item->isEmpty()) {
514
->subElementRect(QStyle::SE_DockWidgetFloatButton,
517
item->setGeometry(r);
522
if (QLayoutItem *item = item_list[Content]) {
524
if (verticalTitleBar) {
525
r.setLeft(_titleArea.right() + 1);
526
r.adjust(0, fw, -fw, -fw);
528
r.setTop(_titleArea.bottom() + 1);
529
r.adjust(fw, 0, -fw, -fw);
531
item->setGeometry(r);
536
void QDockWidgetLayout::setVerticalTitleBar(bool b)
538
if (b == verticalTitleBar)
540
verticalTitleBar = b;
542
parentWidget()->update();
545
/******************************************************************************
549
QDockWidgetItem::QDockWidgetItem(QDockWidget *dockWidget)
550
: QWidgetItem(dockWidget)
554
QSize QDockWidgetItem::minimumSize() const
556
QSize widgetMin(0, 0);
557
if (QLayoutItem *item = dockWidgetChildItem())
558
widgetMin = item->minimumSize();
559
return dockWidgetLayout()->sizeFromContent(widgetMin, false);
562
QSize QDockWidgetItem::maximumSize() const
564
if (QLayoutItem *item = dockWidgetChildItem()) {
565
return dockWidgetLayout()->sizeFromContent(item->maximumSize(), false);
567
return QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
572
QSize QDockWidgetItem::sizeHint() const
574
if (QLayoutItem *item = dockWidgetChildItem()) {
575
return dockWidgetLayout()->sizeFromContent(item->sizeHint(), false);
577
return QWidgetItem::sizeHint();
581
/******************************************************************************
582
** QDockWidgetPrivate
585
void QDockWidgetPrivate::init()
589
QDockWidgetLayout *layout = new QDockWidgetLayout(q);
590
layout->setSizeConstraint(QLayout::SetMinAndMaxSize);
592
QAbstractButton *button = new QDockWidgetTitleButton(q);
593
button->setObjectName(QLatin1String("qt_dockwidget_floatbutton"));
594
QObject::connect(button, SIGNAL(clicked()), q, SLOT(_q_toggleTopLevel()));
595
layout->setWidgetForRole(QDockWidgetLayout::FloatButton, button);
597
button = new QDockWidgetTitleButton(q);
598
button->setObjectName(QLatin1String("qt_dockwidget_closebutton"));
599
QObject::connect(button, SIGNAL(clicked()), q, SLOT(close()));
600
layout->setWidgetForRole(QDockWidgetLayout::CloseButton, button);
602
resizer = new QWidgetResizeHandler(q);
603
resizer->setMovingEnabled(false);
604
resizer->setActive(false);
607
toggleViewAction = new QAction(q);
608
toggleViewAction->setCheckable(true);
609
fixedWindowTitle = qt_setWindowTitle_helperHelper(q->windowTitle(), q);
610
toggleViewAction->setText(fixedWindowTitle);
611
QObject::connect(toggleViewAction, SIGNAL(triggered(bool)),
612
q, SLOT(_q_toggleView(bool)));
619
Initialize \a option with the values from this QDockWidget. This method
620
is useful for subclasses when they need a QStyleOptionDockWidget, but don't want
621
to fill in all the information themselves.
623
\sa QStyleOption::initFrom()
625
void QDockWidget::initStyleOption(QStyleOptionDockWidget *option) const
627
Q_D(const QDockWidget);
631
QDockWidgetLayout *dwlayout = qobject_cast<QDockWidgetLayout*>(layout());
633
option->initFrom(this);
634
option->rect = dwlayout->titleArea();
635
option->title = d->fixedWindowTitle;
636
option->closable = hasFeature(this, QDockWidget::DockWidgetClosable);
637
option->movable = hasFeature(this, QDockWidget::DockWidgetMovable);
638
option->floatable = hasFeature(this, QDockWidget::DockWidgetFloatable);
640
QDockWidgetLayout *l = qobject_cast<QDockWidgetLayout*>(layout());
641
QStyleOptionDockWidgetV2 *v2
642
= qstyleoption_cast<QStyleOptionDockWidgetV2*>(option);
644
v2->verticalTitleBar = l->verticalTitleBar;
647
void QDockWidgetPrivate::_q_toggleView(bool b)
650
if (b == q->isHidden()) {
658
void QDockWidgetPrivate::updateButtons()
661
QDockWidgetLayout *dwLayout = qobject_cast<QDockWidgetLayout*>(layout);
663
QStyleOptionDockWidget opt;
664
q->initStyleOption(&opt);
666
bool customTitleBar = dwLayout->widgetForRole(QDockWidgetLayout::TitleBar) != 0;
667
bool nativeDeco = dwLayout->nativeWindowDeco();
668
bool hideButtons = nativeDeco || customTitleBar;
670
bool canClose = hasFeature(this, QDockWidget::DockWidgetClosable);
671
bool canFloat = hasFeature(this, QDockWidget::DockWidgetFloatable);
673
QAbstractButton *button
674
= qobject_cast<QAbstractButton*>(dwLayout->widgetForRole(QDockWidgetLayout::FloatButton));
675
button->setIcon(q->style()->standardIcon(QStyle::SP_TitleBarNormalButton, &opt, q));
676
button->setVisible(canFloat && !hideButtons);
679
= qobject_cast <QAbstractButton*>(dwLayout->widgetForRole(QDockWidgetLayout::CloseButton));
680
button->setIcon(q->style()->standardIcon(QStyle::SP_TitleBarCloseButton, &opt, q));
681
button->setVisible(canClose && !hideButtons);
683
q->setAttribute(Qt::WA_ContentsPropagated,
684
(canFloat || canClose) && !hideButtons);
686
layout->invalidate();
689
void QDockWidgetPrivate::_q_toggleTopLevel()
692
q->setFloating(!q->isFloating());
695
void QDockWidgetPrivate::initDrag(const QPoint &pos, bool nca)
700
QMainWindow *win = qobject_cast<QMainWindow*>(parent);
702
QMainWindowLayout *layout = qt_mainwindow_layout(win);
703
Q_ASSERT(layout != 0);
704
if (layout->pluggingWidget != 0) // the main window is animating a docking operation
707
state = new QDockWidgetPrivate::DragState;
708
state->pressPos = pos;
709
state->dragging = false;
710
state->widgetItem = 0;
711
state->ownWidgetItem = false;
713
state->ctrlDrag = false;
716
void QDockWidgetPrivate::startDrag()
720
if (state == 0 || state->dragging)
723
QMainWindowLayout *layout = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q->parentWidget()));
724
Q_ASSERT(layout != 0);
726
state->widgetItem = layout->unplug(q);
727
if (state->widgetItem == 0) {
728
/* I have a QMainWindow parent, but I was never inserted with
729
QMainWindow::addDockWidget, so the QMainWindowLayout has no
730
widget item for me. :( I have to create it myself, and then
731
delete it if I don't get dropped into a dock area. */
732
state->widgetItem = new QDockWidgetItem(q);
733
state->ownWidgetItem = true;
739
state->dragging = true;
742
void QDockWidgetPrivate::endDrag(bool abort)
745
Q_ASSERT(state != 0);
749
if (state->dragging) {
750
QMainWindowLayout *mwLayout = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q->parentWidget()));
751
Q_ASSERT(mwLayout != 0);
753
if (abort || !mwLayout->plug(state->widgetItem)) {
754
if (hasFeature(this, QDockWidget::DockWidgetFloatable)) {
755
if (state->ownWidgetItem)
756
delete state->widgetItem;
759
// get rid of the X11BypassWindowManager window flag and activate the resizer
760
Qt::WindowFlags flags = q->windowFlags();
761
flags &= ~Qt::X11BypassWindowManagerHint;
762
q->setWindowFlags(flags);
763
resizer->setActive(QWidgetResizeHandler::Resize, true);
766
QDockWidgetLayout *myLayout
767
= qobject_cast<QDockWidgetLayout*>(layout);
768
resizer->setActive(QWidgetResizeHandler::Resize,
769
myLayout->widgetForRole(QDockWidgetLayout::TitleBar) != 0);
771
undockedGeometry = q->geometry();
774
mwLayout->revert(state->widgetItem);
782
bool QDockWidgetPrivate::isAnimating() const
784
Q_Q(const QDockWidget);
786
QMainWindow *mainWin = qobject_cast<QMainWindow*>(parent);
790
QMainWindowLayout *mainWinLayout = qt_mainwindow_layout(mainWin);
791
if (mainWinLayout == 0)
794
return (void*)mainWinLayout->pluggingWidget == (void*)q;
797
bool QDockWidgetPrivate::mousePressEvent(QMouseEvent *event)
799
#if !defined(QT_NO_MAINWINDOW)
802
QDockWidgetLayout *dwLayout
803
= qobject_cast<QDockWidgetLayout*>(layout);
805
if (!dwLayout->nativeWindowDeco()) {
806
QRect titleArea = dwLayout->titleArea();
808
if (event->button() != Qt::LeftButton ||
809
!titleArea.contains(event->pos()) ||
810
// check if the tool window is movable... do nothing if it
811
// is not (but allow moving if the window is floating)
812
(!hasFeature(this, QDockWidget::DockWidgetMovable) && !q->isFloating()) ||
813
qobject_cast<QMainWindow*>(parent) == 0 ||
814
isAnimating() || state != 0) {
818
initDrag(event->pos(), false);
821
state->ctrlDrag = hasFeature(this, QDockWidget::DockWidgetFloatable) && event->modifiers() & Qt::ControlModifier;
826
#endif // !defined(QT_NO_MAINWINDOW)
830
bool QDockWidgetPrivate::mouseDoubleClickEvent(QMouseEvent *event)
832
QDockWidgetLayout *dwLayout = qobject_cast<QDockWidgetLayout*>(layout);
834
if (!dwLayout->nativeWindowDeco()) {
835
QRect titleArea = dwLayout->titleArea();
837
if (event->button() == Qt::LeftButton && titleArea.contains(event->pos()) &&
838
hasFeature(this, QDockWidget::DockWidgetFloatable)) {
846
bool QDockWidgetPrivate::mouseMoveEvent(QMouseEvent *event)
849
#if !defined(QT_NO_MAINWINDOW)
855
QDockWidgetLayout *dwlayout
856
= qobject_cast<QDockWidgetLayout *>(layout);
857
QMainWindowLayout *mwlayout = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q->parentWidget()));
858
if (!dwlayout->nativeWindowDeco()) {
860
&& mwlayout->pluggingWidget == 0
861
&& (event->pos() - state->pressPos).manhattanLength()
862
> QApplication::startDragDistance()) {
865
grabMouseWhileInWindow();
873
if (state->dragging && !state->nca) {
874
QPoint pos = event->globalPos() - state->pressPos;
877
if (!state->ctrlDrag)
878
mwlayout->hover(state->widgetItem, event->globalPos());
883
#endif // !defined(QT_NO_MAINWINDOW)
887
bool QDockWidgetPrivate::mouseReleaseEvent(QMouseEvent *event)
889
#if !defined(QT_NO_MAINWINDOW)
891
if (event->button() == Qt::LeftButton && state && !state->nca) {
893
return true; //filter out the event
896
#endif // !defined(QT_NO_MAINWINDOW)
900
void QDockWidgetPrivate::nonClientAreaMouseEvent(QMouseEvent *event)
904
int fw = q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, q);
906
QRect geo = q->geometry();
907
QRect titleRect = q->frameGeometry();
909
if ((features & QDockWidget::DockWidgetVerticalTitleBar)) {
910
titleRect.setTop(geo.top());
911
titleRect.setBottom(geo.bottom());
912
titleRect.setRight(geo.left() - 1);
916
titleRect.setLeft(geo.left());
917
titleRect.setRight(geo.right());
918
titleRect.setBottom(geo.top() - 1);
919
titleRect.adjust(0, fw, 0, 0);
922
switch (event->type()) {
923
case QEvent::NonClientAreaMouseButtonPress:
924
if (!titleRect.contains(event->globalPos()))
928
if (qobject_cast<QMainWindow*>(parent) == 0)
932
initDrag(event->pos(), true);
936
// On Windows, NCA mouse events don't contain modifier info
937
state->ctrlDrag = GetKeyState(VK_CONTROL) & 0x8000;
939
state->ctrlDrag = event->modifiers() & Qt::ControlModifier;
943
case QEvent::NonClientAreaMouseMove:
944
if (state == 0 || !state->dragging)
953
case QEvent::NonClientAreaMouseButtonRelease:
959
case QEvent::NonClientAreaMouseButtonDblClick:
967
void QDockWidgetPrivate::moveEvent(QMoveEvent *event)
971
if (state == 0 || !state->dragging || !state->nca || !q->isWindow())
974
// When the native window frame is being dragged, all we get is these mouse
980
QMainWindowLayout *layout = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q->parentWidget()));
981
Q_ASSERT(layout != 0);
983
QPoint globalMousePos = event->pos() + state->pressPos;
984
layout->hover(state->widgetItem, globalMousePos);
987
void QDockWidgetPrivate::unplug(const QRect &rect)
991
r.moveTopLeft(q->mapToGlobal(QPoint(0, 0)));
992
QDockWidgetLayout *dwLayout = qobject_cast<QDockWidgetLayout*>(layout);
993
if (dwLayout->nativeWindowDeco(true))
994
r.adjust(0, dwLayout->titleHeight(), 0, 0);
995
setWindowState(true, true, r);
998
void QDockWidgetPrivate::plug(const QRect &rect)
1000
setWindowState(false, false, rect);
1003
void QDockWidgetPrivate::setWindowState(bool floating, bool unplug, const QRect &rect)
1007
if (!floating && parent) {
1008
QMainWindowLayout *mwlayout = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q->parentWidget()));
1009
if (mwlayout && mwlayout->dockWidgetArea(q) == Qt::NoDockWidgetArea)
1010
return; // this dockwidget can't be redocked
1013
bool wasFloating = q->isFloating();
1014
bool hidden = q->isHidden();
1019
Qt::WindowFlags flags = floating ? Qt::Tool : Qt::Widget;
1021
QDockWidgetLayout *dwLayout = qobject_cast<QDockWidgetLayout*>(layout);
1022
const bool nativeDeco = dwLayout->nativeWindowDeco(floating);
1025
flags |= Qt::CustomizeWindowHint | Qt::WindowTitleHint;
1026
if (hasFeature(this, QDockWidget::DockWidgetClosable))
1027
flags |= Qt::WindowCloseButtonHint;
1029
flags |= Qt::FramelessWindowHint;
1033
flags |= Qt::X11BypassWindowManagerHint;
1035
q->setWindowFlags(flags);
1039
q->setGeometry(rect);
1046
if (floating != wasFloating) {
1047
emit q->topLevelChanged(floating);
1048
if (!floating && parent) {
1049
QMainWindowLayout *mwlayout = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q->parentWidget()));
1051
emit q->dockLocationChanged(mwlayout->dockWidgetArea(q));
1055
if (floating && nativeDeco)
1056
if (const QWindow *window = q->windowHandle())
1057
if (QPlatformWindow *platformWindow = window->handle())
1058
platformWindow->setFrameStrutEventsEnabled(true);
1060
resizer->setActive(QWidgetResizeHandler::Resize, !unplug && floating && !nativeDeco);
1066
\brief The QDockWidget class provides a widget that can be docked
1067
inside a QMainWindow or floated as a top-level window on the
1070
\ingroup mainwindow-classes
1073
QDockWidget provides the concept of dock widgets, also know as
1074
tool palettes or utility windows. Dock windows are secondary
1075
windows placed in the \e {dock widget area} around the
1076
\l{QMainWindow::centralWidget()}{central widget} in a
1079
\image mainwindow-docks.png
1081
Dock windows can be moved inside their current area, moved into
1082
new areas and floated (e.g., undocked) by the end-user. The
1083
QDockWidget API allows the programmer to restrict the dock widgets
1084
ability to move, float and close, as well as the areas in which
1087
\section1 Appearance
1089
A QDockWidget consists of a title bar and the content area. The
1090
title bar displays the dock widgets
1091
\l{QWidget::windowTitle()}{window title},
1092
a \e float button and a \e close button.
1093
Depending on the state of the QDockWidget, the \e float and \e
1094
close buttons may be either disabled or not shown at all.
1096
The visual appearance of the title bar and buttons is dependent
1097
on the \l{QStyle}{style} in use.
1099
A QDockWidget acts as a wrapper for its child widget, set with setWidget().
1100
Custom size hints, minimum and maximum sizes and size policies should be
1101
implemented in the child widget. QDockWidget will respect them, adjusting
1102
its own constraints to include the frame and title. Size constraints
1103
should not be set on the QDockWidget itself, because they change depending
1104
on whether it is docked; a docked QDockWidget has no frame and a smaller title
1107
\sa QMainWindow, {Dock Widgets Example}
1111
\enum QDockWidget::DockWidgetFeature
1113
\value DockWidgetClosable The dock widget can be closed. On some systems the dock
1114
widget always has a close button when it's floating
1115
(for example on MacOS 10.5).
1116
\value DockWidgetMovable The dock widget can be moved between docks
1118
\value DockWidgetFloatable The dock widget can be detached from the
1119
main window, and floated as an independent
1121
\value DockWidgetVerticalTitleBar The dock widget displays a vertical title
1122
bar on its left side. This can be used to
1123
increase the amount of vertical space in
1125
\value AllDockWidgetFeatures (Deprecated) The dock widget can be closed, moved,
1126
and floated. Since new features might be added in future
1127
releases, the look and behavior of dock widgets might
1128
change if you use this flag. Please specify individual
1130
\value NoDockWidgetFeatures The dock widget cannot be closed, moved,
1133
\omitvalue DockWidgetFeatureMask
1138
\property QDockWidget::windowTitle
1139
\brief the dock widget title (caption)
1141
By default, this property contains an empty string.
1145
Constructs a QDockWidget with parent \a parent and window flags \a
1146
flags. The dock widget will be placed in the left dock widget
1149
QDockWidget::QDockWidget(QWidget *parent, Qt::WindowFlags flags)
1150
: QWidget(*new QDockWidgetPrivate, parent, flags)
1157
Constructs a QDockWidget with parent \a parent and window flags \a
1158
flags. The dock widget will be placed in the left dock widget
1161
The window title is set to \a title. This title is used when the
1162
QDockWidget is docked and undocked. It is also used in the context
1163
menu provided by QMainWindow.
1165
\sa setWindowTitle()
1167
QDockWidget::QDockWidget(const QString &title, QWidget *parent, Qt::WindowFlags flags)
1168
: QWidget(*new QDockWidgetPrivate, parent, flags)
1172
setWindowTitle(title);
1176
Destroys the dock widget.
1178
QDockWidget::~QDockWidget()
1182
Returns the widget for the dock widget. This function returns zero
1183
if the widget has not been set.
1187
QWidget *QDockWidget::widget() const
1189
QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(this->layout());
1190
return layout->widgetForRole(QDockWidgetLayout::Content);
1194
Sets the widget for the dock widget to \a widget.
1196
If the dock widget is visible when \a widget is added, you must
1197
\l{QWidget::}{show()} it explicitly.
1199
Note that you must add the layout of the \a widget before you call
1200
this function; if not, the \a widget will not be visible.
1204
void QDockWidget::setWidget(QWidget *widget)
1206
QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(this->layout());
1207
layout->setWidgetForRole(QDockWidgetLayout::Content, widget);
1211
\property QDockWidget::features
1212
\brief whether the dock widget is movable, closable, and floatable
1214
By default, this property is set to a combination of DockWidgetClosable,
1215
DockWidgetMovable and DockWidgetFloatable.
1217
\sa DockWidgetFeature
1220
void QDockWidget::setFeatures(QDockWidget::DockWidgetFeatures features)
1223
features &= DockWidgetFeatureMask;
1224
if (d->features == features)
1226
const bool closableChanged = (d->features ^ features) & DockWidgetClosable;
1227
d->features = features;
1228
QDockWidgetLayout *layout
1229
= qobject_cast<QDockWidgetLayout*>(this->layout());
1230
layout->setVerticalTitleBar(features & DockWidgetVerticalTitleBar);
1232
d->toggleViewAction->setEnabled((d->features & DockWidgetClosable) == DockWidgetClosable);
1233
emit featuresChanged(d->features);
1235
if (closableChanged && layout->nativeWindowDeco()) {
1236
//this ensures the native decoration is drawn
1237
d->setWindowState(true /*floating*/, true /*unplug*/);
1241
QDockWidget::DockWidgetFeatures QDockWidget::features() const
1243
Q_D(const QDockWidget);
1248
\property QDockWidget::floating
1249
\brief whether the dock widget is floating
1251
A floating dock widget is presented to the user as an independent
1252
window "on top" of its parent QMainWindow, instead of being
1253
docked in the QMainWindow.
1255
By default, this property is true.
1259
void QDockWidget::setFloating(bool floating)
1263
// the initial click of a double-click may have started a drag...
1267
QRect r = d->undockedGeometry;
1269
d->setWindowState(floating, false, floating ? r : QRect());
1271
if (floating && r.isNull()) {
1272
if (x() < 0 || y() < 0) //may happen if we have been hidden
1274
setAttribute(Qt::WA_Moved, false); //we want it at the default position
1279
\property QDockWidget::allowedAreas
1280
\brief areas where the dock widget may be placed
1282
The default is Qt::AllDockWidgetAreas.
1284
\sa Qt::DockWidgetArea
1287
void QDockWidget::setAllowedAreas(Qt::DockWidgetAreas areas)
1290
areas &= Qt::DockWidgetArea_Mask;
1291
if (areas == d->allowedAreas)
1293
d->allowedAreas = areas;
1294
emit allowedAreasChanged(d->allowedAreas);
1297
Qt::DockWidgetAreas QDockWidget::allowedAreas() const
1299
Q_D(const QDockWidget);
1300
return d->allowedAreas;
1304
\fn bool QDockWidget::isAreaAllowed(Qt::DockWidgetArea area) const
1306
Returns true if this dock widget can be placed in the given \a area;
1307
otherwise returns false.
1311
void QDockWidget::changeEvent(QEvent *event)
1314
QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(this->layout());
1316
switch (event->type()) {
1317
case QEvent::ModifiedChange:
1318
case QEvent::WindowTitleChange:
1319
update(layout->titleArea());
1320
#ifndef QT_NO_ACTION
1321
d->fixedWindowTitle = qt_setWindowTitle_helperHelper(windowTitle(), this);
1322
d->toggleViewAction->setText(d->fixedWindowTitle);
1324
#ifndef QT_NO_TABBAR
1326
QMainWindow *win = qobject_cast<QMainWindow*>(parentWidget());
1327
if (QMainWindowLayout *winLayout = qt_mainwindow_layout(win)) {
1328
if (QDockAreaLayoutInfo *info = winLayout->layoutState.dockAreaLayout.info(this))
1329
info->updateTabBar();
1332
#endif // QT_NO_TABBAR
1337
QWidget::changeEvent(event);
1341
void QDockWidget::closeEvent(QCloseEvent *event)
1346
QWidget::closeEvent(event);
1350
void QDockWidget::paintEvent(QPaintEvent *event)
1354
QDockWidgetLayout *layout
1355
= qobject_cast<QDockWidgetLayout*>(this->layout());
1356
bool customTitleBar = layout->widgetForRole(QDockWidgetLayout::TitleBar) != 0;
1357
bool nativeDeco = layout->nativeWindowDeco();
1359
if (!nativeDeco && !customTitleBar) {
1360
QStylePainter p(this);
1361
// ### Add PixelMetric to change spacers, so style may show border
1362
// when not floating.
1364
QStyleOptionFrame framOpt;
1366
p.drawPrimitive(QStyle::PE_FrameDockWidget, framOpt);
1369
// Title must be painted after the frame, since the areas overlap, and
1370
// the title may wish to extend out to all sides (eg. XP style)
1371
QStyleOptionDockWidgetV2 titleOpt;
1372
initStyleOption(&titleOpt);
1373
p.drawControl(QStyle::CE_DockWidgetTitle, titleOpt);
1378
bool QDockWidget::event(QEvent *event)
1382
QMainWindow *win = qobject_cast<QMainWindow*>(parentWidget());
1383
QMainWindowLayout *layout = qt_mainwindow_layout(win);
1385
switch (event->type()) {
1386
#ifndef QT_NO_ACTION
1389
layout->keepSize(this);
1390
d->toggleViewAction->setChecked(false);
1391
emit visibilityChanged(false);
1394
d->toggleViewAction->setChecked(true);
1395
emit visibilityChanged(geometry().right() >= 0 && geometry().bottom() >= 0);
1398
case QEvent::ApplicationLayoutDirectionChange:
1399
case QEvent::LayoutDirectionChange:
1400
case QEvent::StyleChange:
1401
case QEvent::ParentChange:
1404
case QEvent::ZOrderChange: {
1407
const QObjectList &siblings = win->children();
1408
onTop = siblings.count() > 0 && siblings.last() == (QObject*)this;
1410
if (!isFloating() && layout != 0 && onTop)
1411
layout->raise(this);
1414
case QEvent::WindowActivate:
1415
case QEvent::WindowDeactivate:
1416
update(qobject_cast<QDockWidgetLayout *>(this->layout())->titleArea());
1418
case QEvent::ContextMenu:
1424
// return true after calling the handler since we don't want
1425
// them to be passed onto the default handlers
1426
case QEvent::MouseButtonPress:
1427
if (d->mousePressEvent(static_cast<QMouseEvent *>(event)))
1430
case QEvent::MouseButtonDblClick:
1431
if (d->mouseDoubleClickEvent(static_cast<QMouseEvent *>(event)))
1434
case QEvent::MouseMove:
1435
if (d->mouseMoveEvent(static_cast<QMouseEvent *>(event)))
1440
if (d->state != 0 && d->state->dragging && !d->state->nca) {
1441
// This is a workaround for loosing the mouse on Vista.
1442
QPoint pos = QCursor::pos();
1443
QMouseEvent fake(QEvent::MouseMove, mapFromGlobal(pos), pos, Qt::NoButton,
1444
QApplication::mouseButtons(), QApplication::keyboardModifiers());
1445
d->mouseMoveEvent(&fake);
1449
case QEvent::MouseButtonRelease:
1450
if (d->mouseReleaseEvent(static_cast<QMouseEvent *>(event)))
1453
case QEvent::NonClientAreaMouseMove:
1454
case QEvent::NonClientAreaMouseButtonPress:
1455
case QEvent::NonClientAreaMouseButtonRelease:
1456
case QEvent::NonClientAreaMouseButtonDblClick:
1457
d->nonClientAreaMouseEvent(static_cast<QMouseEvent*>(event));
1460
d->moveEvent(static_cast<QMoveEvent*>(event));
1462
case QEvent::Resize:
1463
// if the mainwindow is plugging us, we don't want to update undocked geometry
1464
if (isFloating() && layout != 0 && layout->pluggingWidget != this)
1465
d->undockedGeometry = geometry();
1470
return QWidget::event(event);
1473
#ifndef QT_NO_ACTION
1475
Returns a checkable action that can be used to show or close this
1478
The action's text is set to the dock widget's window title.
1480
\sa QAction::text, QWidget::windowTitle
1482
QAction * QDockWidget::toggleViewAction() const
1484
Q_D(const QDockWidget);
1485
return d->toggleViewAction;
1487
#endif // QT_NO_ACTION
1490
\fn void QDockWidget::featuresChanged(QDockWidget::DockWidgetFeatures features)
1492
This signal is emitted when the \l features property changes. The
1493
\a features parameter gives the new value of the property.
1497
\fn void QDockWidget::topLevelChanged(bool topLevel)
1499
This signal is emitted when the \l floating property changes.
1500
The \a topLevel parameter is true if the dock widget is now floating;
1501
otherwise it is false.
1507
\fn void QDockWidget::allowedAreasChanged(Qt::DockWidgetAreas allowedAreas)
1509
This signal is emitted when the \l allowedAreas property changes. The
1510
\a allowedAreas parameter gives the new value of the property.
1514
\fn void QDockWidget::visibilityChanged(bool visible)
1517
This signal is emitted when the dock widget becomes \a visible (or
1518
invisible). This happens when the widget is hidden or shown, as
1519
well as when it is docked in a tabbed dock area and its tab
1520
becomes selected or unselected.
1524
\fn void QDockWidget::dockLocationChanged(Qt::DockWidgetArea area)
1527
This signal is emitted when the dock widget is moved to another
1528
dock \a area, or is moved to a different location in its current
1529
dock area. This happens when the dock widget is moved
1530
programmatically or is dragged to a new location by the user.
1536
Sets an arbitrary \a widget as the dock widget's title bar. If \a widget
1537
is 0, any custom title bar widget previously set on the dock widget is
1538
removed, but not deleted, and the default title bar will be used
1541
If a title bar widget is set, QDockWidget will not use native window
1542
decorations when it is floated.
1544
Here are some tips for implementing custom title bars:
1547
\li Mouse events that are not explicitly handled by the title bar widget
1548
must be ignored by calling QMouseEvent::ignore(). These events then
1549
propagate to the QDockWidget parent, which handles them in the usual
1550
manner, moving when the title bar is dragged, docking and undocking
1551
when it is double-clicked, etc.
1553
\li When DockWidgetVerticalTitleBar is set on QDockWidget, the title
1554
bar widget is repositioned accordingly. In resizeEvent(), the title
1555
bar should check what orientation it should assume:
1556
\snippet code/src_gui_widgets_qdockwidget.cpp 0
1558
\li The title bar widget must have a valid QWidget::sizeHint() and
1559
QWidget::minimumSizeHint(). These functions should take into account
1560
the current orientation of the title bar.
1562
\li It is not possible to remove a title bar from a dock widget. However,
1563
a similar effect can be achieved by setting a default constructed
1564
QWidget as the title bar widget.
1567
Using qobject_cast() as shown above, the title bar widget has full access
1568
to its parent QDockWidget. Hence it can perform such operations as docking
1569
and hiding in response to user actions.
1571
\sa titleBarWidget(), DockWidgetVerticalTitleBar
1574
void QDockWidget::setTitleBarWidget(QWidget *widget)
1577
QDockWidgetLayout *layout
1578
= qobject_cast<QDockWidgetLayout*>(this->layout());
1579
layout->setWidgetForRole(QDockWidgetLayout::TitleBar, widget);
1582
//this ensures the native decoration is drawn
1583
d->setWindowState(true /*floating*/, true /*unplug*/);
1589
Returns the custom title bar widget set on the QDockWidget, or 0 if no
1590
custom title bar has been set.
1592
\sa setTitleBarWidget()
1595
QWidget *QDockWidget::titleBarWidget() const
1597
QDockWidgetLayout *layout
1598
= qobject_cast<QDockWidgetLayout*>(this->layout());
1599
return layout->widgetForRole(QDockWidgetLayout::TitleBar);
1604
#include "qdockwidget.moc"
1605
#include "moc_qdockwidget.cpp"
1607
#endif // QT_NO_DOCKWIDGET