📄 qdockwidget.cpp
字号:
/******************************************************************************** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.**** This file is part of the QtGui module of the Qt Toolkit.**** This file may be used under the terms of the GNU General Public** License version 2.0 as published by the Free Software Foundation** and appearing in the file LICENSE.GPL included in the packaging of** this file. Please review the following information to ensure GNU** General Public Licensing requirements will be met:** http://www.trolltech.com/products/qt/opensource.html**** If you are unsure which license is appropriate for your use, please** review the following information:** http://www.trolltech.com/products/qt/licensing.html or contact the** sales department at sales@trolltech.com.**** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.******************************************************************************/#include "qdockwidget.h"#ifndef QT_NO_DOCKWIDGET#include <qaction.h>#include <qapplication.h>#include <qdesktopwidget.h>#include <qdrawutil.h>#include <qevent.h>#include <qfontmetrics.h>#include <qmainwindow.h>#include <qpainter.h>#include <qrubberband.h>#include <qstyle.h>#include <qstyleoption.h>#include <qtoolbutton.h>#include <qdebug.h>#include <private/qwidgetresizehandler_p.h>#include "qdockwidget_p.h"#include "qdockwidgetlayout_p.h"#include "qmainwindowlayout_p.h"#ifdef Q_WS_MAC#include <qmacstyle_mac.h>#endifstatic inline bool hasFeature(QDockWidget *dockwidget, QDockWidget::DockWidgetFeature feature){ return (dockwidget->features() & feature) == feature; }/* A Dock Window: [+] is the float button [X] is the close button +-------------------------------+ | Dock Window Title [+][X]| +-------------------------------+ | | | place to put the single | | QDockWidget child (this space | | does not yet have a name) | | | | | | | | | | | | | | | | | | | +-------------------------------+*//* Tool window title*/class QDockWidgetTitleButton : public QAbstractButton{ Q_OBJECTpublic: QDockWidgetTitleButton(QDockWidget *dockWidget); QSize sizeHint() const; inline QSize minimumSizeHint() const { return sizeHint(); } void enterEvent(QEvent *event); void leaveEvent(QEvent *event); void paintEvent(QPaintEvent *event);};QDockWidgetTitleButton::QDockWidgetTitleButton(QDockWidget *dockWidget) : QAbstractButton(dockWidget){ setFocusPolicy(Qt::NoFocus); }QSize QDockWidgetTitleButton::sizeHint() const{ ensurePolished(); int dim = 0; if (!icon().isNull()) { const QPixmap pm = icon().pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize), QIcon::Normal); dim = qMax(pm.width(), pm.height()); } return QSize(dim + 4, dim + 4);}void QDockWidgetTitleButton::enterEvent(QEvent *event){ if (isEnabled()) update(); QAbstractButton::enterEvent(event);}void QDockWidgetTitleButton::leaveEvent(QEvent *event){ if (isEnabled()) update(); QAbstractButton::leaveEvent(event);}void QDockWidgetTitleButton::paintEvent(QPaintEvent *){ QPainter p(this); QRect r = rect(); QStyleOption opt; opt.init(this); opt.state |= QStyle::State_AutoRaise; if (isEnabled() && underMouse() && !isChecked() && !isDown()) opt.state |= QStyle::State_Raised; if (isChecked()) opt.state |= QStyle::State_On; if (isDown()) opt.state |= QStyle::State_Sunken; style()->drawPrimitive(QStyle::PE_PanelButtonTool, &opt, &p, this); r.adjust(2, 2, -2, -2); QPixmap pm = icon().pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize), isEnabled() ? underMouse() ? QIcon::Active : QIcon::Normal : QIcon::Disabled, isDown() ? QIcon::On : QIcon::Off); style()->drawItemPixmap(&p, r, Qt::AlignCenter, pm);}/* Private class*/void QDockWidgetPrivate::init() { Q_Q(QDockWidget); top = new QGridLayout(q); top->setMargin(0); top->setSpacing(0); box = new QDockWidgetBoxLayout; box->setMargin(0); box->setSpacing(0); top->addLayout(box, 1, 0); topSpacer = new QSpacerItem(0, 20, QSizePolicy::Ignored, QSizePolicy::Fixed); box->addItem(topSpacer); resizer = new QWidgetResizeHandler(q); resizer->setMovingEnabled(false); resizer->setActive(false);#ifndef QT_NO_ACTION toggleViewAction = new QAction(q); toggleViewAction->setCheckable(true); toggleViewAction->setText(q->windowTitle()); QObject::connect(toggleViewAction, SIGNAL(triggered(bool)), q, SLOT(_q_toggleView(bool)));#endif updateButtons();}QStyleOptionDockWidget QDockWidgetPrivate::getStyleOption(){ Q_Q(QDockWidget); QStyleOptionDockWidget opt; opt.init(q); opt.rect = titleArea; opt.title = q->windowTitle(); opt.closable = hasFeature(q, QDockWidget::DockWidgetClosable); opt.movable = hasFeature(q, QDockWidget::DockWidgetMovable); opt.floatable = hasFeature(q, QDockWidget::DockWidgetFloatable); return opt;}void QDockWidgetPrivate::_q_toggleView(bool b){ Q_Q(QDockWidget); if (b == q->isHidden()) { if (b) q->show(); else q->close(); }}void QDockWidgetPrivate::updateButtons(){ Q_Q(QDockWidget); if (hasFeature(q, QDockWidget::DockWidgetFloatable)) { if (!floatButton) { floatButton = new QDockWidgetTitleButton(q); QObject::connect(floatButton, SIGNAL(clicked()), q, SLOT(_q_toggleTopLevel())); if (!q->isHidden()) floatButton->show(); } } else { delete floatButton; floatButton = 0; } if (hasFeature(q, QDockWidget::DockWidgetClosable)) { if (!closeButton) { closeButton = new QDockWidgetTitleButton(q); QObject::connect(closeButton, SIGNAL(clicked()), q, SLOT(close())); if (!q->isHidden()) closeButton->show(); } } else { delete closeButton; closeButton = 0; } bool anyButton = (floatButton || closeButton); if (anyButton) { QStyleOptionDockWidget opt = getStyleOption(); if (floatButton) floatButton->setIcon(q->style()->standardIcon(QStyle::SP_TitleBarNormalButton, &opt, q)); if (closeButton) closeButton->setIcon(q->style()->standardIcon(QStyle::SP_TitleBarCloseButton, &opt, q)); } q->setAttribute(Qt::WA_ContentsPropagated, anyButton); relayout();}// ### Todo 4.1: Add subrects to style API, this will cover our styles for now// Also, add posibilty to get standardIconsvoid QDockWidgetPrivate::relayout(){ Q_Q(QDockWidget); int fw = q->isFloating() ? q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, q) : 0; int mw = q->style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, 0, q); QSize closeSize = closeButton ? closeButton->sizeHint() : QSize(0,0); QSize floatSize = floatButton ? floatButton->sizeHint() : QSize(0,0); int minWidth = q->fontMetrics().width(q->windowTitle()) + 2 * fw + 2 * mw; int minHeight = qMax(closeSize.width(), closeSize.height()) + 2 * mw; minHeight = qMax(minHeight, qMax(floatSize.width(), floatSize.height())); minHeight += 2; // Allow 1px frame around title area with buttons inside#ifdef Q_WS_MAC if (qobject_cast<QMacStyle *>(q->style())) { extern QHash<QByteArray, QFont> *qt_app_fonts_hash(); // qapplication.cpp QFont font = qt_app_fonts_hash()->value("QToolButton", q->font()); QFontMetrics fm(font); minHeight = qMax(minHeight, fm.lineSpacing() + 2 + 2 * mw) - fw; //Ensure 2 px margin around font } else#endif { minHeight = qMax(minHeight, q->fontMetrics().lineSpacing() + 2 + 2 * mw) - fw; //Ensure 2 px margin around font } titleArea = QRect(QPoint(fw, fw), QSize(q->rect().width() - (fw * 2), minHeight)); int posX = titleArea.right(); QPoint buttonOffset(0, 0);#ifdef Q_OS_WIN //### Fix this properly in Qt 4.2 if (q->style()->inherits("QWindowsXPStyle")) { if(q->isFloating()) buttonOffset = QPoint(2, -1); else buttonOffset = QPoint(0, 1); }#endif if (closeButton) { //### Fix this properly in Qt 4.2 closeButton->setGeometry(QStyle::visualRect( qApp->layoutDirection(), titleArea, QRect(posX - closeSize.width() - mw + buttonOffset.x(), titleArea.center().y() - closeSize.height() / 2 + + buttonOffset.y(), closeSize.width(), closeSize.height()))); posX -= closeSize.width() + 1; } if (floatButton) { //### Fix this properly in Qt 4.2 floatButton->setGeometry(QStyle::visualRect( qApp->layoutDirection(), titleArea, QRect(posX - floatSize.width() - mw + buttonOffset.x(), titleArea.center().y() - floatSize.height() / 2 + buttonOffset.y(), floatSize.width(), floatSize.height()))); posX -= floatSize.width() + 1; } topSpacer->changeSize(minWidth, 0 + titleArea.height(), QSizePolicy::Expanding, QSizePolicy::Fixed); top->setMargin(fw); top->invalidate();}void QDockWidgetPrivate::_q_toggleTopLevel(){ Q_Q(QDockWidget); QPoint p = q->mapToGlobal(QPoint(titleArea.height(), titleArea.height())); bool visible = q->isVisible(); if (visible) q->hide(); q->setFloating(!q->isFloating()); if (q->isWindow()) q->move(p); if (visible) q->show();}QMainWindow *QDockWidgetPrivate::findMainWindow(QWidget *widget) const{ Q_Q(const QDockWidget); QMainWindow *mainwindow = 0; // look for our QMainWindow while (widget && !mainwindow) { if (widget == q->parentWidget()) { // found our parent widget, has to be the mainwindow we're looking for mainwindow = qobject_cast<QMainWindow *>(widget); break; } else if (widget->isWindow()) { // found a window that isn't our QMainWindow, stop looking widget = 0; } else { widget = widget->parentWidget(); } } return mainwindow;}void QDockWidgetPrivate::mousePressEvent(QMouseEvent *event){#if !defined(QT_NO_MAINWINDOW) Q_Q(QDockWidget); if (event->button() != Qt::LeftButton) return; if (!titleArea.contains(event->pos())) return; // check if the tool window is movable... do nothing if it is not if (!::hasFeature(q, QDockWidget::DockWidgetMovable)) return; if (!q->parentWidget()) return; QMainWindowLayout *layout = qobject_cast<QMainWindowLayout *>(q->parentWidget()->layout()); if (!layout) return; layout->saveLayoutInfo(); Q_ASSERT(!state); state = new QDockWidgetPrivate::DragState; state->rubberband = 0; // the current location of the tool window in global coordinates state->origin = QRect(q->mapToGlobal(QPoint(0, 0)), q->size()); state->current = state->origin; const QPoint globalPos = event->globalPos(); const int dl = globalPos.x() - state->current.left(), dr = state->current.right() - globalPos.x(), halfWidth = state->origin.width() / 2; state->offset = q->mapFrom(q, (dl < dr) ? QPoint(qMin(dl, halfWidth), 0) : QPoint(state->origin.width() - qMin(dr, halfWidth) - 1, 0)); state->offset = q->mapTo(q, QPoint(state->offset.x(), event->pos().y())); state->canDrop = true;#ifdef Q_WS_WIN /* Work around windows expose bug when windows are partially covered by * a top level transparent object. */ q->update(); QWidgetList children = qFindChildren<QWidget *>(q); for (int i=0; i<children.size(); ++i) children.at(i)->update();#endif#endif // !defined(QT_NO_MAINWINDOW)}void QDockWidgetPrivate::mouseDoubleClickEvent(QMouseEvent *event){ Q_Q(QDockWidget); if (event->button() != Qt::LeftButton) return; if (!titleArea.contains(event->pos())) return; if (!::hasFeature(q, QDockWidget::DockWidgetFloatable)) return; _q_toggleTopLevel();}void QDockWidgetPrivate::mouseMoveEvent(QMouseEvent *event){#if !defined(QT_NO_MAINWINDOW) Q_Q(QDockWidget); if (!state) return; QRect target; if (!(event->modifiers() & Qt::ControlModifier)) { // see if there is a main window under us, and ask it to place the tool window QWidget *widget = QApplication::widgetAt(event->globalPos()); if (widget) { QMainWindow *mainwindow = findMainWindow(widget); if (mainwindow) { QMainWindowLayout *layout = qobject_cast<QMainWindowLayout *>(q->parentWidget()->layout()); Q_ASSERT(layout != 0); QRect request = state->origin; // ### remove extra frame request.moveTopLeft(event->globalPos() - state->offset); target = layout->placeDockWidget(q, request, event->globalPos()); layout->resetLayoutInfo(); } } } state->canDrop = target.isValid(); if (!state->canDrop) { if (hasFeature(q, QDockWidget::DockWidgetFloatable)) { /* main window refused to accept the tool window,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -