📄 qabstractscrollarea.cpp
字号:
/******************************************************************************** Copyright (C) 1992-2007 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://trolltech.com/products/qt/licenses/licensing/opensource/**** If you are unsure which license is appropriate for your use, please** review the following information:** http://trolltech.com/products/qt/licenses/licensing/licensingoverview** or contact the sales department at sales@trolltech.com.**** In addition, as a special exception, Trolltech gives you certain** additional rights. These rights are described in the Trolltech GPL** Exception version 1.0, which can be found at** http://www.trolltech.com/products/qt/gplexception/ and in the file** GPL_EXCEPTION.txt in this package.**** In addition, as a special exception, Trolltech, as the sole copyright** holder for Qt Designer, grants users of the Qt/Eclipse Integration** plug-in the right for the Qt/Eclipse Integration to link to** functionality provided by Qt Designer and its related libraries.**** Trolltech reserves all rights not expressly granted herein.**** 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 "qabstractscrollarea.h"#ifndef QT_NO_SCROLLAREA#include "qscrollbar.h"#include "qapplication.h"#include "qstyle.h"#include "qstyleoption.h"#include "qevent.h"#include "qdebug.h"#include "qboxlayout.h"#include "qpainter.h"#include "qabstractscrollarea_p.h"#include <qwidget.h>#ifdef Q_WS_MAC#include <qmacstyle_mac.h>#include <private/qt_mac_p.h>#endif/*! \class QAbstractScrollArea \brief The QAbstractScrollArea widget provides a scrolling area with on-demand scroll bars. \ingroup abstractwidgets QAbstractScrollArea is a low-level abstraction of a scrolling area. The area provides a central widget called the viewport, in which the contents of the area is to be scrolled (i.e, the visible parts of the contents are rendered in the viewport). Next to the viewport is a vertical scroll bar, and below is a horizontal scroll bar. When all of the area contents fits in the viewport, each scroll bar can be either visible or hidden depending on the scroll bar's Qt::ScrollBarPolicy. When a scroll bar is hidden, the viewport expands in order to cover all available space. When a scroll bar becomes visible again, the viewport shrinks in order to make room for the scroll bar. It is possible to reserve a margin area around the viewport, see setViewportMargins(). The feature is mostly used to place a QHeaderView widget above or beside the scrolling area. Subclasses of QAbstractScrollArea should implement margins. When inheriting QAbstractScrollArea, you need to do the following: \list \o Control the scroll bars by setting their range, value, page step, and tracking their movements. \o Draw the contents of the area in the viewport according to the values of the scroll bars. \o Handle events received by the viewport in viewportEvent() - notably resize events. \endlist With a scroll bar policy of Qt::ScrollBarAsNeeded (the default), QAbstractScrollArea shows scroll bars when they provide a non-zero scrolling range, and hides them otherwise. The scroll bars and viewport should be updated whenever the viewport receives a resize event or the size of the contents changes. The viewport also needs to be updated when the scroll bars values change. The initial values of the scroll bars are often set when the area receives new contents. We give a simple example, in which we have implemented a scroll area that can scroll any QWidget. We make the widget a child of the viewport; this way, we do not have to calculate which part of the widget to draw but can simply move the widget with QWidget::move(). When the area contents or the viewport size changes, we do the following: \quotefromfile snippets/myscrollarea.cpp \skipto areaSize \printto /^\}/ When the scroll bars change value, we need to update the widget position, i.e., find the part of the widget that is to be drawn in the viewport: \quotefromfile snippets/myscrollarea.cpp \skipto hvalue \printto /^\}/ In order to track scroll bar movements, reimplement the virtual function scrollContentsBy(). In order to fine-tune scrolling behavior, connect to a scroll bar's QAbstractSlider::actionTriggered() signal and adjust the \l QAbstractSlider::sliderPosition as you wish. For convenience, QAbstractScrollArea makes all viewport events available in the virtual viewportEvent() handler. QWidget's specialized handlers are remapped to viewport events in the cases where this makes sense. The remapped specialized handlers are: paintEvent(), mousePressEvent(), mouseReleaseEvent(), mouseDoubleClickEvent(), mouseMoveEvent(), wheelEvent(), dragEnterEvent(), dragMoveEvent(), dragLeaveEvent(), dropEvent(), contextMenuEvent(), and resizeEvent(). QScrollArea, which inherits QAbstractScrollArea, provides smooth scrolling for any QWidget (i.e., the widget is scrolled pixel by pixel). You only need to subclass QAbstractScrollArea if you need more specialized behavior. This is, for instance, true if the entire contents of the area is not suitable for being drawn on a QWidget or if you do not want smooth scrolling. \sa QScrollArea*/QAbstractScrollAreaPrivate::QAbstractScrollAreaPrivate() :hbar(0), vbar(0), vbarpolicy(Qt::ScrollBarAsNeeded), hbarpolicy(Qt::ScrollBarAsNeeded), viewport(0), cornerWidget(0), left(0), top(0), right(0), bottom(0), xoffset(0), yoffset(0), viewportFilter(0){}QAbstractScrollAreaScrollBarContainer::QAbstractScrollAreaScrollBarContainer(Qt::Orientation orientation, QWidget *parent) :QWidget(parent), scrollBar(new QScrollBar(orientation, this)), layout(new QBoxLayout(orientation == Qt::Horizontal ? QBoxLayout::LeftToRight : QBoxLayout::TopToBottom)), orientation(orientation){ setLayout(layout); layout->setMargin(0); layout->setSpacing(0); layout->addWidget(scrollBar);}/*! \internal Adds a widget to the scroll bar container.*/void QAbstractScrollAreaScrollBarContainer::addWidget(QWidget *widget, LogicalPosition position){ QSizePolicy policy = widget->sizePolicy(); if (orientation == Qt::Vertical) policy.setHorizontalPolicy(QSizePolicy::Ignored); else policy.setVerticalPolicy(QSizePolicy::Ignored); widget->setSizePolicy(policy); widget->setParent(this); const int insertIndex = (position & LogicalLeft) ? 0 : scrollBarLayoutIndex() + 1; layout->insertWidget(insertIndex, widget);}/*! \internal Retuns a list of scroll bar widgets for the given position. The scroll bar itself is not returned.*/QWidgetList QAbstractScrollAreaScrollBarContainer::widgets(LogicalPosition position){ QWidgetList list; const int scrollBarIndex = scrollBarLayoutIndex(); if (position == LogicalLeft) { for (int i = 0; i < scrollBarIndex; ++i) list.append(layout->itemAt(i)->widget()); } else if (position == LogicalRight) { const int layoutItemCount = layout->count(); for (int i = scrollBarIndex + 1; i < layoutItemCount; ++i) list.append(layout->itemAt(i)->widget()); } return list;}/*! \internal Returns the layout index for the scroll bar. This needs to be recalculated by a linear search for each use, since items in the layout can be removed at any time (i.e. when a widget is deleted or re-parented).*/int QAbstractScrollAreaScrollBarContainer::scrollBarLayoutIndex() const{ const int layoutItemCount = layout->count(); for (int i = 0; i < layoutItemCount; ++i) { if (qobject_cast<QScrollBar *>(layout->itemAt(i)->widget())) return i; } return -1;}/*! \internal*/void QAbstractScrollAreaPrivate::replaceScrollBar(QScrollBar *scrollBar, Qt::Orientation orientation){ Q_Q(QAbstractScrollArea); QAbstractScrollAreaScrollBarContainer *container = scrollBarContainers[orientation]; bool horizontal = (orientation == Qt::Horizontal); QScrollBar *oldBar = horizontal ? hbar : vbar; if (horizontal) hbar = scrollBar; else vbar = scrollBar; scrollBar->setParent(container); container->scrollBar = scrollBar; container->layout->removeWidget(oldBar); container->layout->insertWidget(0, scrollBar); scrollBar->setVisible(oldBar->isVisibleTo(oldBar->window())); scrollBar->setInvertedAppearance(oldBar->invertedAppearance()); scrollBar->setInvertedControls(oldBar->invertedControls()); scrollBar->setRange(oldBar->minimum(), oldBar->maximum()); scrollBar->setOrientation(oldBar->orientation()); scrollBar->setPageStep(oldBar->pageStep()); scrollBar->setSingleStep(oldBar->singleStep()); scrollBar->setSliderDown(oldBar->isSliderDown()); scrollBar->setSliderPosition(oldBar->sliderPosition()); scrollBar->setTracking(oldBar->hasTracking()); scrollBar->setValue(oldBar->value()); delete oldBar; QObject::connect(scrollBar, SIGNAL(valueChanged(int)), q, horizontal ? SLOT(_q_hslide(int)) : SLOT(_q_vslide(int))); QObject::connect(scrollBar, SIGNAL(rangeChanged(int,int)), q, SLOT(_q_showOrHideScrollBars()), Qt::QueuedConnection);}void QAbstractScrollAreaPrivate::init(){ Q_Q(QAbstractScrollArea); scrollBarContainers[Qt::Horizontal] = new QAbstractScrollAreaScrollBarContainer(Qt::Horizontal, q); scrollBarContainers[Qt::Horizontal]->setObjectName(QLatin1String("qt_scrollarea_hcontainer")); hbar = scrollBarContainers[Qt::Horizontal]->scrollBar; hbar->setRange(0,0); scrollBarContainers[Qt::Horizontal]->setVisible(false); QObject::connect(hbar, SIGNAL(valueChanged(int)), q, SLOT(_q_hslide(int))); QObject::connect(hbar, SIGNAL(rangeChanged(int,int)), q, SLOT(_q_showOrHideScrollBars()), Qt::QueuedConnection); scrollBarContainers[Qt::Vertical] = new QAbstractScrollAreaScrollBarContainer(Qt::Vertical, q); scrollBarContainers[Qt::Vertical]->setObjectName(QLatin1String("qt_scrollarea_vcontainer")); vbar = scrollBarContainers[Qt::Vertical]->scrollBar; vbar->setRange(0,0); scrollBarContainers[Qt::Vertical]->setVisible(false); QObject::connect(vbar, SIGNAL(valueChanged(int)), q, SLOT(_q_vslide(int))); QObject::connect(vbar, SIGNAL(rangeChanged(int,int)), q, SLOT(_q_showOrHideScrollBars()), Qt::QueuedConnection); viewportFilter = new QAbstractScrollAreaFilter(this); viewport = new QWidget(q); viewport->setObjectName(QLatin1String("qt_scrollarea_viewport")); viewport->setBackgroundRole(QPalette::Base); viewport->setAutoFillBackground(true); viewport->installEventFilter(viewportFilter); viewport->setFocusProxy(q); q->setFocusPolicy(Qt::WheelFocus); q->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); q->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); layoutChildren();}void QAbstractScrollAreaPrivate::layoutChildren(){ Q_Q(QAbstractScrollArea); bool needh = (hbarpolicy == Qt::ScrollBarAlwaysOn || (hbarpolicy == Qt::ScrollBarAsNeeded && hbar->minimum() < hbar->maximum())); bool needv = (vbarpolicy == Qt::ScrollBarAlwaysOn || (vbarpolicy == Qt::ScrollBarAsNeeded && vbar->minimum() < vbar->maximum())); const int hsbExt = hbar->sizeHint().height(); const int vsbExt = vbar->sizeHint().width(); const QPoint extPoint(vsbExt, hsbExt); const QSize extSize(vsbExt, hsbExt); const QRect widgetRect = q->rect(); QStyleOption opt(0); opt.init(q); const bool hasCornerWidget = (cornerWidget != 0);// If the scroll bars are at the very right and bottom of the window we// move their positions to be aligned with the size grip.#ifdef Q_WS_MAC QWidget * const window = q->window(); // Check if a native sizegrip is present. bool hasMacReverseSizeGrip = false; bool hasMacSizeGrip = false; HIViewRef nativeSizeGrip; HIViewFindByID(HIViewGetRoot(HIViewGetWindow(HIViewRef(q->winId()))), kHIViewWindowGrowBoxID, &nativeSizeGrip); if (nativeSizeGrip) { // Look for a native size grip at the visual window bottom right and at the // absolute window bottom right. In reverse mode, the native size grip does not // swich side, so we need to check if it is on the "wrong side". const QPoint scrollAreaBottomRight = q->mapTo(window, widgetRect.bottomRight() - QPoint(frameWidth, frameWidth)); const QPoint windowBottomRight = window->rect().bottomRight(); const QPoint visualWindowBottomRight = QStyle::visualPos(opt.direction, opt.rect, windowBottomRight); const QPoint offset = windowBottomRight - scrollAreaBottomRight; const QPoint visualOffset = visualWindowBottomRight - scrollAreaBottomRight; hasMacSizeGrip = (visualOffset.manhattanLength() < vsbExt); hasMacReverseSizeGrip = (hasMacSizeGrip == false && (offset.manhattanLength() < hsbExt)); } // Use small scroll bars for tool windows, to match the native size grip. const QMacStyle::WidgetSizePolicy hpolicy = QMacStyle::widgetSizePolicy(hbar); const QMacStyle::WidgetSizePolicy vpolicy = QMacStyle::widgetSizePolicy(vbar); const Qt::WindowType windowType = window->windowType(); if (windowType == Qt::Tool) { if (hpolicy != QMacStyle::SizeSmall) QMacStyle::setWidgetSizePolicy(hbar, QMacStyle::SizeSmall); if (vpolicy != QMacStyle::SizeSmall) QMacStyle::setWidgetSizePolicy(vbar, QMacStyle::SizeSmall); } else { if (hpolicy != QMacStyle::SizeDefault) QMacStyle::setWidgetSizePolicy(hbar, QMacStyle::SizeDefault); if (vpolicy != QMacStyle::SizeDefault) QMacStyle::setWidgetSizePolicy(vbar, QMacStyle::SizeDefault); }#endif QPoint cornerOffset(needv ? vsbExt : 0, needh ? hsbExt : 0); QRect controlsRect; QRect viewportRect; // In FrameOnlyAroundContents mode the frame is drawn between the controls and // the viewport, else the frame rect is equal to the widget rect. if (q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, &opt, q)) { controlsRect = widgetRect; const int extra = q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth) * 2; const QPoint cornerExtra(needv ? extra : 0, needh ? extra : 0); QRect frameRect = widgetRect; frameRect.adjust(0, 0, -cornerOffset.x() - cornerExtra.x(), -cornerOffset.y() - cornerExtra.y()); q->setFrameRect(frameRect); viewportRect = q->contentsRect(); } else { q->setFrameRect(QStyle::visualRect(opt.direction, opt.rect, widgetRect)); controlsRect = q->contentsRect(); viewportRect = QRect(controlsRect.topLeft(), controlsRect.bottomRight() - cornerOffset); } // If we have a corner widget and are only showing one scroll bar, we need to move it // to make room for the corner widget. if (hasCornerWidget && (needv || needh)) cornerOffset = extPoint;#ifdef Q_WS_MAC // Also move the scroll bars if they are covered by the native Mac size grip. if (hasMacSizeGrip) cornerOffset = extPoint;#endif // The corner point is where the scroll bar rects, the corner widget rect and the // viewport rect meets. const QPoint cornerPoint(controlsRect.bottomRight() + QPoint(1, 1) - cornerOffset); // Some styles paints the corner if both scorllbars are showing and there is // no corner widget. Also, on the Mac we paint if there is a native // (transparent) sizegrip in the area where a corner widget would be. if (needv && needh && hasCornerWidget == false#ifdef Q_WS_MAC || ((needv || needh) && hasMacSizeGrip)#endif ) { cornerPaintingRect = QStyle::visualRect(opt.direction, opt.rect, QRect(cornerPoint, extSize)); } else { cornerPaintingRect = QRect(); }#ifdef Q_WS_MAC if (hasMacReverseSizeGrip) reverseCornerPaintingRect = QRect(controlsRect.bottomRight() + QPoint(1, 1) - extPoint, extSize); else reverseCornerPaintingRect = QRect();#endif if (needh) { QRect horizontalScrollBarRect(QPoint(controlsRect.left(), cornerPoint.y()), QPoint(cornerPoint.x() - 1, controlsRect.bottom()));#ifdef Q_WS_MAC if (hasMacReverseSizeGrip) horizontalScrollBarRect.adjust(vsbExt, 0, 0, 0);#endif scrollBarContainers[Qt::Horizontal]->setGeometry(QStyle::visualRect(opt.direction, opt.rect, horizontalScrollBarRect)); scrollBarContainers[Qt::Horizontal]->raise(); } if (needv) { const QRect verticalScrollBarRect (QPoint(cornerPoint.x(), controlsRect.top()), QPoint(controlsRect.right(), cornerPoint.y() - 1)); scrollBarContainers[Qt::Vertical]->setGeometry(QStyle::visualRect(opt.direction, opt.rect, verticalScrollBarRect)); scrollBarContainers[Qt::Vertical]->raise(); } if (cornerWidget) { const QRect cornerWidgetRect(cornerPoint, controlsRect.bottomRight()); cornerWidget->setGeometry(QStyle::visualRect(opt.direction, opt.rect, cornerWidgetRect)); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -