1
/****************************************************************************
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
5
** This file is part of the widgets module of the Qt Toolkit.
7
** This file may be distributed under the terms of the Q Public License
8
** as defined by Trolltech AS of Norway and appearing in the file
9
** LICENSE.QPL included in the packaging of this file.
11
** This file may be distributed and/or modified under the terms of the
12
** GNU General Public License version 2 as published by the Free Software
13
** Foundation and appearing in the file LICENSE.GPL included in the
14
** packaging of this file.
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
17
** information about Qt Commercial License Agreements.
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
21
** Contact info@trolltech.com if any conditions of this licensing are
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27
****************************************************************************/
29
#include "qdockwidget.h"
32
#include <qapplication.h>
33
#include <qdesktopwidget.h>
34
#include <qdrawutil.h>
36
#include <qfontmetrics.h>
37
#include <qmainwindow.h>
39
#include <qrubberband.h>
41
#include <qstyleoption.h>
42
#include <qtoolbutton.h>
44
#include <private/qwidgetresizehandler_p.h>
46
#include "qdockwidget_p.h"
47
#include "qdockwidgetlayout_p.h"
48
#include "qmainwindowlayout_p.h"
51
inline bool hasFeature(QDockWidget *dockwidget, QDockWidget::DockWidgetFeature feature)
52
{ return (dockwidget->features() & feature) == feature; }
58
[+] is the float button
59
[X] is the close button
61
+-------------------------------+
62
| Dock Window Title [+][X]|
63
+-------------------------------+
65
| place to put the single |
66
| QDockWidget child (this space |
67
| does not yet have a name) |
77
+-------------------------------+
88
class QDockWidgetTitle;
90
class QDockWidgetTitleButton : public QAbstractButton
95
QDockWidgetTitleButton(QDockWidgetTitle *title);
97
QSize sizeHint() const;
98
inline QSize minimumSizeHint() const
99
{ return sizeHint(); }
101
void enterEvent(QEvent *event);
102
void leaveEvent(QEvent *event);
103
void paintEvent(QPaintEvent *event);
106
class QDockWidgetTitle : public QWidget
111
QDockWidgetTitle(QDockWidget *tw);
113
void styleChange(QStyle &);
115
void mousePressEvent(QMouseEvent *event);
116
void mouseMoveEvent(QMouseEvent *event);
117
void mouseReleaseEvent(QMouseEvent *event);
118
void contextMenuEvent(QContextMenuEvent *event);
119
void paintEvent(QPaintEvent *event);
121
void updateButtons();
122
void updateWindowTitle();
124
QDockWidget *dockwidget;
127
QDockWidgetTitleButton *floatButton;
128
QDockWidgetTitleButton *closeButton;
133
QRubberBand *rubberband;
134
QRect origin; // starting position
135
QRect current; // current size of the dockwidget (can be either placed or floating)
136
QRect floating; // size of the floating dockwidget
143
void toggleTopLevel();
146
QDockWidgetTitleButton::QDockWidgetTitleButton(QDockWidgetTitle *title)
147
: QAbstractButton(title)
148
{ setFocusPolicy(Qt::NoFocus); }
150
QSize QDockWidgetTitleButton::sizeHint() const
155
if (!icon().isNull()) {
156
const QPixmap pm = icon().pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize), QIcon::Normal);
157
dim = qMax(pm.width(), pm.height());
160
return QSize(dim + 4, dim + 4);
163
void QDockWidgetTitleButton::enterEvent(QEvent *event)
165
if (isEnabled()) update();
166
QAbstractButton::enterEvent(event);
169
void QDockWidgetTitleButton::leaveEvent(QEvent *event)
171
if (isEnabled()) update();
172
QAbstractButton::leaveEvent(event);
175
void QDockWidgetTitleButton::paintEvent(QPaintEvent *)
182
opt.state |= QStyle::State_AutoRaise;
183
if (isEnabled() && underMouse() && !isChecked() && !isDown())
184
opt.state |= QStyle::State_Raised;
186
opt.state |= QStyle::State_On;
188
opt.state |= QStyle::State_Sunken;
189
style()->drawPrimitive(QStyle::PE_PanelButtonTool, &opt, &p, this);
191
r.adjust(2, 2, -2, -2);
192
QPixmap pm = icon().pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize), isEnabled() ? QIcon::Normal : QIcon::Disabled);
193
style()->drawItemPixmap(&p, r, Qt::AlignCenter, pm);
196
QDockWidgetTitle::QDockWidgetTitle(QDockWidget *tw)
197
: QWidget(tw), dockwidget(tw), floatButton(0), closeButton(0), state(0)
199
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
201
spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed);
203
box = new QBoxLayout(QBoxLayout::LeftToRight, this);
204
box->setMargin(style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth));
206
box->addItem(spacer);
212
void QDockWidgetTitle::styleChange(QStyle &)
214
box->setMargin(style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth));
219
floatButton->setIcon(style()->standardPixmap(QStyle::SP_TitleBarMaxButton));
222
closeButton->setIcon(style()->standardPixmap(QStyle::SP_TitleBarCloseButton));
225
void QDockWidgetTitle::mousePressEvent(QMouseEvent *event)
227
if (event->button() != Qt::LeftButton) return;
229
// check if the tool window is movable... do nothing if it is not
230
if (!::hasFeature(dockwidget, QDockWidget::DockWidgetMovable))
233
QMainWindowLayout *layout =
234
qobject_cast<QMainWindowLayout *>(dockwidget->parentWidget()->layout());
237
layout->saveLayoutInfo();
240
state = new DragState;
242
const int screen_number = QApplication::desktop()->screenNumber(window());
243
state->rubberband = new QRubberBand(QRubberBand::Rectangle,
244
QApplication::desktop()->screen(screen_number));
246
// the current location of the tool window in global coordinates
247
state->origin = QRect(dockwidget->mapToGlobal(QPoint(0, 0)), dockwidget->size());
248
state->current = state->origin;
250
// like the above, except using the tool window's size hint
251
state->floating = dockwidget->isWindow()
253
: QRect(state->current.topLeft(), dockwidget->sizeHint());
255
const QPoint globalPos = event->globalPos();
256
const int dl = globalPos.x() - state->current.left(),
257
dr = state->current.right() - globalPos.x(),
258
halfWidth = state->floating.width() / 2;
259
state->offset = mapFrom(dockwidget,
261
? QPoint(qMin(dl, halfWidth), 0)
262
: QPoint(state->floating.width() - qMin(dr, halfWidth), 0));
263
state->offset = mapTo(dockwidget, QPoint(state->offset.x(), event->pos().y()));
265
state->canDrop = true;
267
state->rubberband->setGeometry(state->current);
268
state->rubberband->show();
271
/* Work around windows expose bug when windows are partially covered by
272
* a top level transparent object.
274
dockwidget->update();
275
QWidgetList children = qFindChildren<QWidget *>(dockwidget);
276
for (int i=0; i<children.size(); ++i)
277
children.at(i)->update();
281
void QDockWidgetTitle::mouseMoveEvent(QMouseEvent *event)
288
if (!(event->modifiers() & Qt::ControlModifier)) {
289
// see if there is a main window under us, and ask it to place the tool window
290
QWidget *widget = QApplication::widgetAt(event->globalPos());
292
while (widget && !qobject_cast<QMainWindow *>(widget)) {
293
if (widget->isWindow()) {
297
widget = widget->parentWidget();
301
QMainWindow *mainwindow = qobject_cast<QMainWindow *>(widget);
302
if (mainwindow && mainwindow == dockwidget->parentWidget()) {
303
QMainWindowLayout *layout =
304
qobject_cast<QMainWindowLayout *>(dockwidget->parentWidget()->layout());
305
Q_ASSERT(layout != 0);
306
QRect request = state->origin;
307
request.moveTopLeft(event->globalPos() - state->offset);
308
target = layout->placeDockWidget(dockwidget, request, event->globalPos());
309
layout->resetLayoutInfo();
315
state->canDrop = target.isValid();
316
if (!state->canDrop) {
317
if (hasFeature(dockwidget, QDockWidget::DockWidgetFloatable)) {
319
main window refused to accept the tool window,
320
recalculate absolute position as if the tool window
321
was to be dropped to toplevel
323
target = state->floating;
324
target.moveTopLeft(event->globalPos() - state->offset);
327
cannot float the window, so put it back into it's
330
target = state->origin;
334
if (state->current == target)
337
state->rubberband->setGeometry(target);
338
state->current = target;
341
void QDockWidgetTitle::mouseReleaseEvent(QMouseEvent *event)
343
if (event->button() != Qt::LeftButton)
348
QMainWindowLayout *layout =
349
qobject_cast<QMainWindowLayout *>(dockwidget->parentWidget()->layout());
352
layout->discardLayoutInfo();
354
delete state->rubberband;
356
QWidget *focus = qApp->focusWidget();
358
// calculate absolute position if the tool window was to be
359
// dropped to toplevel
361
bool dropped = false;
362
if (!(event->modifiers() & Qt::ControlModifier)) {
363
// see if there is a main window under us, and ask it to drop the tool window
364
QWidget *widget = QApplication::widgetAt(event->globalPos());
365
if (state->canDrop && widget) {
366
while (widget && !qobject_cast<QMainWindow *>(widget)) {
367
if (widget->isWindow()) {
371
widget = widget->parentWidget();
375
QMainWindow *mainwindow = qobject_cast<QMainWindow *>(widget);
376
if (mainwindow && mainwindow == dockwidget->parentWidget()) {
377
QMainWindowLayout *layout =
378
qobject_cast<QMainWindowLayout *>(dockwidget->parentWidget()->layout());
379
Q_ASSERT(layout != 0);
380
QRect request = state->origin;
381
request.moveTopLeft(event->globalPos() - state->offset);
382
layout->dropDockWidget(dockwidget, request, event->globalPos());
389
if (!dropped && hasFeature(dockwidget, QDockWidget::DockWidgetFloatable)) {
390
target = state->floating;
391
target.moveTopLeft(event->globalPos() - state->offset);
393
if (!dockwidget->isFloating()) {
395
dockwidget->setFloating(true);
396
dockwidget->setGeometry(target);
399
// move to new location
400
dockwidget->setGeometry(target);
412
void QDockWidgetTitle::contextMenuEvent(QContextMenuEvent *event)
417
QWidget::contextMenuEvent(event);
420
void QDockWidgetTitle::paintEvent(QPaintEvent *)
424
QStyleOptionDockWidget opt;
426
opt.palette = palette();
428
opt.state |= QStyle::State_Enabled;
430
opt.state |= QStyle::State_MouseOver;
432
opt.title = dockwidget->windowTitle();
433
opt.closable = hasFeature(dockwidget, QDockWidget::DockWidgetClosable);
434
opt.movable = hasFeature(dockwidget, QDockWidget::DockWidgetMovable);
435
opt.floatable = hasFeature(dockwidget, QDockWidget::DockWidgetFloatable);
436
style()->drawControl(QStyle::CE_DockWidgetTitle, &opt, &p, this);
439
void QDockWidgetTitle::updateButtons()
441
if (hasFeature(dockwidget, QDockWidget::DockWidgetFloatable)) {
443
floatButton = new QDockWidgetTitleButton(this);
444
floatButton->setIcon(style()->standardPixmap(QStyle::SP_TitleBarMaxButton));
445
connect(floatButton, SIGNAL(clicked()), SLOT(toggleTopLevel()));
447
box->insertWidget(1, floatButton);
449
if (!dockwidget->isHidden())
457
if (hasFeature(dockwidget, QDockWidget::DockWidgetClosable)) {
459
closeButton = new QDockWidgetTitleButton(this);
460
closeButton->setIcon(style()->standardPixmap(QStyle::SP_TitleBarCloseButton));
461
connect(closeButton, SIGNAL(clicked()), dockwidget, SLOT(close()));
463
box->insertWidget(2, closeButton);
465
if (!dockwidget->isHidden())
474
void QDockWidgetTitle::updateWindowTitle()
476
const QFontMetrics fm(font());
477
spacer->changeSize(fm.width(dockwidget->windowTitle()) + box->margin() * 2, fm.lineSpacing(),
478
QSizePolicy::Expanding, QSizePolicy::Fixed);
483
void QDockWidgetTitle::toggleTopLevel()
485
QPoint p = dockwidget->mapToGlobal(QPoint(height(), height()));
486
bool visible = dockwidget->isVisible();
489
dockwidget->setFloating(!dockwidget->isFloating());
490
if (dockwidget->isWindow())
504
void QDockWidgetPrivate::init() {
507
int fw = q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth);
508
top = new QVBoxLayout(q);
510
top->setMargin(qMax(fw, 3));
512
title = new QDockWidgetTitle(q);
513
top->insertWidget(0, title);
515
box = new QVBoxLayout;
518
resizer = new QWidgetResizeHandler(q);
519
resizer->setMovingEnabled(false);
520
resizer->setActive(false);
522
toggleViewAction = new QAction(q);
523
toggleViewAction->setCheckable(true);
524
toggleViewAction->setText(q->windowTitle());
525
QObject::connect(toggleViewAction, SIGNAL(triggered(bool)), q, SLOT(toggleView(bool)));
528
void QDockWidgetPrivate::toggleView(bool b)
531
if (b == q->isHidden()) {
544
\brief The QDockWidget class provides a widget that can be docked
545
inside a QMainWindow or floated as a top-level window on the
550
QDockWidget provides the concept of dock widgets, also know as
551
tool palettes or utility windows. Dock windows are secondary
552
windows placed in the \e {dock widget area} around the
553
\l{QMainWindow::centralWidget()}{central widget} in a
556
\image mainwindow-docks.png
558
Dock windows can be moved inside their current area, moved into
559
new areas and floated (e.g. undocked) by the end-user. The
560
QDockWidget API allows the programmer to restrict the dock widgets
561
ability to move, float and close, as well as the areas in which
566
A QDockWidget consists of a title bar and the content area. The
567
titlebar displays the dock widgets \link QWidget::windowTitle()
568
window title\endlink, a \e float button and a \e close button.
569
Depending on the state of the QDockWidget, the \e float and \e
570
close buttons may be either disabled or not shown at all.
572
The visual appearance of the title bar and buttons is dependent
573
on the \l{QStyle}{style} in use.
579
\enum QDockWidget::DockWidgetFeature
581
\value DockWidgetClosable The dock widget can be closed.
582
\value DockWidgetMovable The dock widget can be moved between docks
584
\value DockWidgetFloatable The dock widget can be detached from the
585
main window, and floated as an independent
588
\value AllDockWidgetFeatures The dock widget can be closed, moved,
590
\value NoDockWidgetFeatures The dock widget cannot be closed, moved,
593
\omitvalue DockWidgetFeatureMask
598
Constructs a QDockWidget with parent \a parent and window flags \a
599
flags. The dock widget will be placed in the left dock widget
602
QDockWidget::QDockWidget(QWidget *parent, Qt::WFlags flags)
603
: QWidget(*new QDockWidgetPrivate, parent, flags)
610
Constructs a QDockWidget with parent \a parent and window flags \a
611
flags. The dock widget will be placed in the left dock widget
614
The window title is set to \a title. This title is used when the
615
QDockWidget is docked and undocked. It is also used in the context
616
menu provided by QMainWindow.
620
QDockWidget::QDockWidget(const QString &title, QWidget *parent, Qt::WFlags flags)
621
: QWidget(*new QDockWidgetPrivate, parent, flags)
625
setWindowTitle(title);
629
Destroys the dock widget.
631
QDockWidget::~QDockWidget()
635
Returns the widget for the dock widget. This function returns zero
636
if the widget has not been set.
640
QWidget *QDockWidget::widget() const
642
Q_D(const QDockWidget);
647
Sets the widget for the dock widget to \a widget.
651
void QDockWidget::setWidget(QWidget *widget)
655
d->box->removeWidget(d->widget);
658
d->box->insertWidget(1, d->widget);
662
\property QDockWidget::features
663
\brief whether the dock widget is movable, closable, and floatable
665
\sa DockWidgetFeature
668
void QDockWidget::setFeatures(QDockWidget::DockWidgetFeatures features)
671
features &= DockWidgetFeatureMask;
672
if (d->features == features)
674
d->features = features;
675
d->title->updateButtons();
677
emit featuresChanged(d->features);
680
QDockWidget::DockWidgetFeatures QDockWidget::features() const
682
Q_D(const QDockWidget);
687
\property QDockWidget::floating
688
\brief whether the dock widget is floating
690
A floating dock widget is presented to the user as an independent
691
window "on top" of its parent QMainWindow, instead of being
692
docked in the QMainWindow.
696
void QDockWidget::setFloating(bool floating)
699
if (floating == isFloating())
702
const bool visible = isVisible();
704
setWindowFlags(Qt::FramelessWindowHint | (floating ? Qt::Tool : Qt::Widget));
707
if (QMainWindowLayout *layout = qobject_cast<QMainWindowLayout *>(parentWidget()->layout()))
708
layout->invalidate();
711
d->resizer->setActive(floating);
716
emit topLevelChanged(isWindow());
720
\property QDockWidget::allowedAreas
721
\brief areas where the dock widget may be placed
723
The default is Qt::AllDockWidgetAreas.
725
\sa Qt::DockWidgetArea
728
void QDockWidget::setAllowedAreas(Qt::DockWidgetAreas areas)
731
areas &= Qt::DockWidgetArea_Mask;
732
if (areas == d->allowedAreas)
734
d->allowedAreas = areas;
735
emit allowedAreasChanged(d->allowedAreas);
738
Qt::DockWidgetAreas QDockWidget::allowedAreas() const
740
Q_D(const QDockWidget);
741
return d->allowedAreas;
745
\fn bool QDockWidget::isAreaAllowed(Qt::DockWidgetArea area) const
747
Returns true if this dock widget can be placed in the given \a area;
748
otherwise returns false.
752
void QDockWidget::changeEvent(QEvent *event)
755
switch (event->type()) {
756
case QEvent::WindowTitleChange:
757
d->title->updateWindowTitle();
758
d->toggleViewAction->setText(windowTitle());
763
QWidget::changeEvent(event);
767
void QDockWidget::closeEvent(QCloseEvent *event)
770
if (!(d->features & DockWidgetClosable))
775
void QDockWidget::paintEvent(QPaintEvent *event)
778
QStyleOptionFrame opt;
781
style()->drawPrimitive(QStyle::PE_FrameDockWidget, &opt, &p, this);
785
bool QDockWidget::event(QEvent *event)
788
switch (event->type()) {
792
// fallthrough intended
794
d->toggleViewAction->setChecked(event->type() == QEvent::Show);
796
case QEvent::StyleChange: {
797
int fw = style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth);
798
d->top->setSpacing(fw);
799
d->top->setMargin(qMax(fw, 3));
804
return QWidget::event(event);
808
Returns a checkable action that can be used to show or close this
811
The action's text is set to the dock widget's window title.
813
\sa QAction::text QWidget::windowTitle
815
QAction * QDockWidget::toggleViewAction() const
817
Q_D(const QDockWidget);
818
return d->toggleViewAction;
822
\fn void QDockWidget::featuresChanged(DockWidgetFeatures features)
824
This signal is emitted when the \l features property changes. The
825
\a features parameter gives the new value of the property.
829
\fn void QDockWidget::topLevelChanged(bool topLevel)
831
This signal is emitted when the \l floating property changes.
832
The \a topLevel parameter is true if the dock widget is now floating;
833
otherwise it is false.
839
\fn void QDockWidget::allowedAreasChanged(Qt::DockWidgetAreas allowedAreas)
841
This signal is emitted when the \l allowedAreas property changes. The
842
\a allowedAreas parameter gives the new value of the property.
845
#include "qdockwidget.moc"
846
#include "moc_qdockwidget.cpp"