| /**************************************************************************** |
| ** |
| ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). |
| ** All rights reserved. |
| ** Contact: Nokia Corporation (qt-info@nokia.com) |
| ** |
| ** This file is part of the QtGui module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** GNU Lesser General Public License Usage |
| ** This file may be used under the terms of the GNU Lesser General Public |
| ** License version 2.1 as published by the Free Software Foundation and |
| ** appearing in the file LICENSE.LGPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU Lesser |
| ** General Public License version 2.1 requirements will be met: |
| ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
| ** |
| ** In addition, as a special exception, Nokia gives you certain additional |
| ** rights. These rights are described in the Nokia Qt LGPL Exception |
| ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU General |
| ** Public License version 3.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 the GNU General |
| ** Public License version 3.0 requirements will be met: |
| ** http://www.gnu.org/copyleft/gpl.html. |
| ** |
| ** Other Usage |
| ** Alternatively, this file may be used in accordance with the terms and |
| ** conditions contained in a signed written agreement between you and Nokia. |
| ** |
| ** |
| ** |
| ** |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qapplication.h" |
| #include "qcursor.h" |
| #include "qevent.h" |
| #include "qpainter.h" |
| #include "qscrollbar.h" |
| #include "qstyle.h" |
| #include "qstyleoption.h" |
| #include "qmenu.h" |
| #include <QtCore/qelapsedtimer.h> |
| |
| #ifndef QT_NO_SCROLLBAR |
| |
| #ifndef QT_NO_ACCESSIBILITY |
| #include "qaccessible.h" |
| #endif |
| #include <limits.h> |
| #include "qabstractslider_p.h" |
| |
| QT_BEGIN_NAMESPACE |
| |
| /*! |
| \class QScrollBar |
| \brief The QScrollBar widget provides a vertical or horizontal scroll bar. |
| |
| \ingroup basicwidgets |
| |
| A scroll bar is a control that enables the user to access parts of a |
| document that is larger than the widget used to display it. It provides |
| a visual indication of the user's current position within the document |
| and the amount of the document that is visible. Scroll bars are usually |
| equipped with other controls that enable more accurate navigation. |
| Qt displays scroll bars in a way that is appropriate for each platform. |
| |
| If you need to provide a scrolling view onto another widget, it may be |
| more convenient to use the QScrollArea class because this provides a |
| viewport widget and scroll bars. QScrollBar is useful if you need to |
| implement similar functionality for specialized widgets using QAbstractScrollArea; |
| for example, if you decide to subclass QAbstractItemView. |
| For most other situations where a slider control is used to obtain a value |
| within a given range, the QSlider class may be more appropriate for your |
| needs. |
| |
| \table |
| \row \o \image qscrollbar-picture.png |
| \o Scroll bars typically include four separate controls: a slider, |
| scroll arrows, and a page control. |
| |
| \list |
| \o a. The slider provides a way to quickly go to any part of the |
| document, but does not support accurate navigation within large |
| documents. |
| \o b. The scroll arrows are push buttons which can be used to accurately |
| navigate to a particular place in a document. For a vertical scroll bar |
| connected to a text editor, these typically move the current position one |
| "line" up or down, and adjust the position of the slider by a small |
| amount. In editors and list boxes a "line" might mean one line of text; |
| in an image viewer it might mean 20 pixels. |
| \o c. The page control is the area over which the slider is dragged (the |
| scroll bar's background). Clicking here moves the scroll bar towards |
| the click by one "page". This value is usually the same as the length of |
| the slider. |
| \endlist |
| \endtable |
| |
| Each scroll bar has a value that indicates how far the slider is from |
| the start of the scroll bar; this is obtained with value() and set |
| with setValue(). This value always lies within the range of values |
| defined for the scroll bar, from \l{QAbstractSlider::minimum()}{minimum()} |
| to \l{QAbstractSlider::minimum()}{maximum()} inclusive. The range of |
| acceptable values can be set with setMinimum() and setMaximum(). |
| At the minimum value, the top edge of the slider (for a vertical scroll |
| bar) or left edge (for a horizontal scroll bar) will be at the top (or |
| left) end of the scroll bar. At the maximum value, the bottom (or right) |
| edge of the slider will be at the bottom (or right) end of the scroll bar. |
| |
| The length of the slider is usually related to the value of the page step, |
| and typically represents the proportion of the document area shown in a |
| scrolling view. The page step is the amount that the value changes by |
| when the user presses the \key{Page Up} and \key{Page Down} keys, and is |
| set with setPageStep(). Smaller changes to the value defined by the |
| line step are made using the cursor keys, and this quantity is set with |
| \l{QAbstractSlider::}{setSingleStep()}. |
| |
| Note that the range of values used is independent of the actual size |
| of the scroll bar widget. You do not need to take this into account when |
| you choose values for the range and the page step. |
| |
| The range of values specified for the scroll bar are often determined |
| differently to those for a QSlider because the length of the slider |
| needs to be taken into account. If we have a document with 100 lines, |
| and we can only show 20 lines in a widget, we may wish to construct a |
| scroll bar with a page step of 20, a minimum value of 0, and a maximum |
| value of 80. This would give us a scroll bar with five "pages". |
| |
| \table |
| \row \o \inlineimage qscrollbar-values.png |
| \o The relationship between a document length, the range of values used |
| in a scroll bar, and the page step is simple in many common situations. |
| The scroll bar's range of values is determined by subtracting a |
| chosen page step from some value representing the length of the document. |
| In such cases, the following equation is useful: |
| \e{document length} = maximum() - minimum() + pageStep(). |
| \endtable |
| |
| QScrollBar only provides integer ranges. Note that although |
| QScrollBar handles very large numbers, scroll bars on current |
| screens cannot usefully represent ranges above about 100,000 pixels. |
| Beyond that, it becomes difficult for the user to control the |
| slider using either the keyboard or the mouse, and the scroll |
| arrows will have limited use. |
| |
| ScrollBar inherits a comprehensive set of signals from QAbstractSlider: |
| \list |
| \o \l{QAbstractSlider::valueChanged()}{valueChanged()} is emitted when the |
| scroll bar's value has changed. The tracking() determines whether this |
| signal is emitted during user interaction. |
| \o \l{QAbstractSlider::rangeChanged()}{rangeChanged()} is emitted when the |
| scroll bar's range of values has changed. |
| \o \l{QAbstractSlider::sliderPressed()}{sliderPressed()} is emitted when |
| the user starts to drag the slider. |
| \o \l{QAbstractSlider::sliderMoved()}{sliderMoved()} is emitted when the user |
| drags the slider. |
| \o \l{QAbstractSlider::sliderReleased()}{sliderReleased()} is emitted when |
| the user releases the slider. |
| \o \l{QAbstractSlider::actionTriggered()}{actionTriggered()} is emitted |
| when the scroll bar is changed by user interaction or via the |
| \l{QAbstractSlider::triggerAction()}{triggerAction()} function. |
| \endlist |
| |
| A scroll bar can be controlled by the keyboard, but it has a |
| default focusPolicy() of Qt::NoFocus. Use setFocusPolicy() to |
| enable keyboard interaction with the scroll bar: |
| \list |
| \o Left/Right move a horizontal scroll bar by one single step. |
| \o Up/Down move a vertical scroll bar by one single step. |
| \o PageUp moves up one page. |
| \o PageDown moves down one page. |
| \o Home moves to the start (mininum). |
| \o End moves to the end (maximum). |
| \endlist |
| |
| The slider itself can be controlled by using the |
| \l{QAbstractSlider::triggerAction()}{triggerAction()} function to simulate |
| user interaction with the scroll bar controls. This is useful if you have |
| many different widgets that use a common range of values. |
| |
| Most GUI styles use the pageStep() value to calculate the size of the |
| slider. |
| |
| \table 100% |
| \row \o \inlineimage macintosh-horizontalscrollbar.png Screenshot of a Macintosh style scroll bar |
| \o A scroll bar shown in the \l{Macintosh Style Widget Gallery}{Macintosh widget style}. |
| \row \o \inlineimage windowsxp-horizontalscrollbar.png Screenshot of a Windows XP style scroll bar |
| \o A scroll bar shown in the \l{Windows XP Style Widget Gallery}{Windows XP widget style}. |
| \row \o \inlineimage plastique-horizontalscrollbar.png Screenshot of a Plastique style scroll bar |
| \o A scroll bar shown in the \l{Plastique Style Widget Gallery}{Plastique widget style}. |
| \endtable |
| |
| \sa QScrollArea, QSlider, QDial, QSpinBox, {fowler}{GUI Design Handbook: Scroll Bar}, {Sliders Example} |
| */ |
| |
| class QScrollBarPrivate : public QAbstractSliderPrivate |
| { |
| Q_DECLARE_PUBLIC(QScrollBar) |
| public: |
| QStyle::SubControl pressedControl; |
| bool pointerOutsidePressedControl; |
| |
| int clickOffset, snapBackPosition; |
| |
| void activateControl(uint control, int threshold = 500); |
| void stopRepeatAction(); |
| int pixelPosToRangeValue(int pos) const; |
| void init(); |
| bool updateHoverControl(const QPoint &pos); |
| QStyle::SubControl newHoverControl(const QPoint &pos); |
| |
| QStyle::SubControl hoverControl; |
| QRect hoverRect; |
| }; |
| |
| bool QScrollBarPrivate::updateHoverControl(const QPoint &pos) |
| { |
| Q_Q(QScrollBar); |
| QRect lastHoverRect = hoverRect; |
| QStyle::SubControl lastHoverControl = hoverControl; |
| bool doesHover = q->testAttribute(Qt::WA_Hover); |
| if (lastHoverControl != newHoverControl(pos) && doesHover) { |
| q->update(lastHoverRect); |
| q->update(hoverRect); |
| return true; |
| } |
| return !doesHover; |
| } |
| |
| QStyle::SubControl QScrollBarPrivate::newHoverControl(const QPoint &pos) |
| { |
| Q_Q(QScrollBar); |
| QStyleOptionSlider opt; |
| q->initStyleOption(&opt); |
| opt.subControls = QStyle::SC_All; |
| hoverControl = q->style()->hitTestComplexControl(QStyle::CC_ScrollBar, &opt, pos, q); |
| if (hoverControl == QStyle::SC_None) |
| hoverRect = QRect(); |
| else |
| hoverRect = q->style()->subControlRect(QStyle::CC_ScrollBar, &opt, hoverControl, q); |
| return hoverControl; |
| } |
| |
| void QScrollBarPrivate::activateControl(uint control, int threshold) |
| { |
| QAbstractSlider::SliderAction action = QAbstractSlider::SliderNoAction; |
| switch (control) { |
| case QStyle::SC_ScrollBarAddPage: |
| action = QAbstractSlider::SliderPageStepAdd; |
| break; |
| case QStyle::SC_ScrollBarSubPage: |
| action = QAbstractSlider::SliderPageStepSub; |
| break; |
| case QStyle::SC_ScrollBarAddLine: |
| action = QAbstractSlider::SliderSingleStepAdd; |
| break; |
| case QStyle::SC_ScrollBarSubLine: |
| action = QAbstractSlider::SliderSingleStepSub; |
| break; |
| case QStyle::SC_ScrollBarFirst: |
| action = QAbstractSlider::SliderToMinimum; |
| break; |
| case QStyle::SC_ScrollBarLast: |
| action = QAbstractSlider::SliderToMaximum; |
| break; |
| default: |
| break; |
| } |
| |
| if (action) { |
| q_func()->setRepeatAction(action, threshold); |
| q_func()->triggerAction(action); |
| } |
| } |
| |
| void QScrollBarPrivate::stopRepeatAction() |
| { |
| Q_Q(QScrollBar); |
| QStyle::SubControl tmp = pressedControl; |
| q->setRepeatAction(QAbstractSlider::SliderNoAction); |
| pressedControl = QStyle::SC_None; |
| |
| if (tmp == QStyle::SC_ScrollBarSlider) |
| q->setSliderDown(false); |
| |
| QStyleOptionSlider opt; |
| q->initStyleOption(&opt); |
| q->repaint(q->style()->subControlRect(QStyle::CC_ScrollBar, &opt, tmp, q)); |
| } |
| |
| /*! |
| Initialize \a option with the values from this QScrollBar. This method |
| is useful for subclasses when they need a QStyleOptionSlider, but don't want |
| to fill in all the information themselves. |
| |
| \sa QStyleOption::initFrom() |
| */ |
| void QScrollBar::initStyleOption(QStyleOptionSlider *option) const |
| { |
| if (!option) |
| return; |
| |
| Q_D(const QScrollBar); |
| option->initFrom(this); |
| option->subControls = QStyle::SC_None; |
| option->activeSubControls = QStyle::SC_None; |
| option->orientation = d->orientation; |
| option->minimum = d->minimum; |
| option->maximum = d->maximum; |
| option->sliderPosition = d->position; |
| option->sliderValue = d->value; |
| option->singleStep = d->singleStep; |
| option->pageStep = d->pageStep; |
| option->upsideDown = d->invertedAppearance; |
| if (d->orientation == Qt::Horizontal) |
| option->state |= QStyle::State_Horizontal; |
| } |
| |
| |
| #define HORIZONTAL (d_func()->orientation == Qt::Horizontal) |
| #define VERTICAL !HORIZONTAL |
| |
| /*! |
| Constructs a vertical scroll bar. |
| |
| The \a parent argument is sent to the QWidget constructor. |
| |
| The \l {QAbstractSlider::minimum} {minimum} defaults to 0, the |
| \l {QAbstractSlider::maximum} {maximum} to 99, with a |
| \l {QAbstractSlider::singleStep} {singleStep} size of 1 and a |
| \l {QAbstractSlider::pageStep} {pageStep} size of 10, and an |
| initial \l {QAbstractSlider::value} {value} of 0. |
| */ |
| QScrollBar::QScrollBar(QWidget *parent) |
| : QAbstractSlider(*new QScrollBarPrivate, parent) |
| { |
| d_func()->orientation = Qt::Vertical; |
| d_func()->init(); |
| } |
| |
| /*! |
| Constructs a scroll bar with the given \a orientation. |
| |
| The \a parent argument is passed to the QWidget constructor. |
| |
| The \l {QAbstractSlider::minimum} {minimum} defaults to 0, the |
| \l {QAbstractSlider::maximum} {maximum} to 99, with a |
| \l {QAbstractSlider::singleStep} {singleStep} size of 1 and a |
| \l {QAbstractSlider::pageStep} {pageStep} size of 10, and an |
| initial \l {QAbstractSlider::value} {value} of 0. |
| */ |
| QScrollBar::QScrollBar(Qt::Orientation orientation, QWidget *parent) |
| : QAbstractSlider(*new QScrollBarPrivate, parent) |
| { |
| d_func()->orientation = orientation; |
| d_func()->init(); |
| } |
| |
| |
| #ifdef QT3_SUPPORT |
| /*! |
| Use one of the constructors that doesn't take the \a name |
| argument and then use setObjectName() instead. |
| */ |
| QScrollBar::QScrollBar(QWidget *parent, const char *name) |
| : QAbstractSlider(*new QScrollBarPrivate, parent) |
| { |
| setObjectName(QString::fromAscii(name)); |
| d_func()->orientation = Qt::Vertical; |
| d_func()->init(); |
| } |
| |
| /*! |
| Use one of the constructors that doesn't take the \a name |
| argument and then use setObjectName() instead. |
| */ |
| QScrollBar::QScrollBar(Qt::Orientation orientation, QWidget *parent, const char *name) |
| : QAbstractSlider(*new QScrollBarPrivate, parent) |
| { |
| setObjectName(QString::fromAscii(name)); |
| d_func()->orientation = orientation; |
| d_func()->init(); |
| } |
| |
| /*! |
| Use one of the constructors that doesn't take the \a name |
| argument and then use setObjectName() instead. |
| */ |
| QScrollBar::QScrollBar(int minimum, int maximum, int lineStep, int pageStep, |
| int value, Qt::Orientation orientation, |
| QWidget *parent, const char *name) |
| : QAbstractSlider(*new QScrollBarPrivate, parent) |
| { |
| Q_D(QScrollBar); |
| setObjectName(QString::fromAscii(name)); |
| d->minimum = minimum; |
| d->maximum = maximum; |
| d->singleStep = lineStep; |
| d->pageStep = pageStep; |
| d->value = value; |
| d->orientation = orientation; |
| d->init(); |
| } |
| #endif // QT3_SUPPORT |
| |
| /*! |
| Destroys the scroll bar. |
| */ |
| QScrollBar::~QScrollBar() |
| { |
| } |
| |
| void QScrollBarPrivate::init() |
| { |
| Q_Q(QScrollBar); |
| invertedControls = true; |
| pressedControl = hoverControl = QStyle::SC_None; |
| pointerOutsidePressedControl = false; |
| q->setFocusPolicy(Qt::NoFocus); |
| QSizePolicy sp(QSizePolicy::Minimum, QSizePolicy::Fixed, QSizePolicy::Slider); |
| if (orientation == Qt::Vertical) |
| sp.transpose(); |
| q->setSizePolicy(sp); |
| q->setAttribute(Qt::WA_WState_OwnSizePolicy, false); |
| q->setAttribute(Qt::WA_OpaquePaintEvent); |
| |
| #if !defined(QT_NO_CONTEXTMENU) && defined(Q_WS_WINCE) |
| if (!q->style()->styleHint(QStyle::SH_ScrollBar_ContextMenu, 0, q)) { |
| q->setContextMenuPolicy(Qt::PreventContextMenu); |
| } |
| #endif |
| } |
| |
| #ifndef QT_NO_CONTEXTMENU |
| /*! \reimp */ |
| void QScrollBar::contextMenuEvent(QContextMenuEvent *event) |
| { |
| if (!style()->styleHint(QStyle::SH_ScrollBar_ContextMenu, 0, this)) { |
| QAbstractSlider::contextMenuEvent(event); |
| return ; |
| } |
| |
| #ifndef QT_NO_MENU |
| bool horiz = HORIZONTAL; |
| QPointer<QMenu> menu = new QMenu(this); |
| QAction *actScrollHere = menu->addAction(tr("Scroll here")); |
| menu->addSeparator(); |
| QAction *actScrollTop = menu->addAction(horiz ? tr("Left edge") : tr("Top")); |
| QAction *actScrollBottom = menu->addAction(horiz ? tr("Right edge") : tr("Bottom")); |
| menu->addSeparator(); |
| QAction *actPageUp = menu->addAction(horiz ? tr("Page left") : tr("Page up")); |
| QAction *actPageDn = menu->addAction(horiz ? tr("Page right") : tr("Page down")); |
| menu->addSeparator(); |
| QAction *actScrollUp = menu->addAction(horiz ? tr("Scroll left") : tr("Scroll up")); |
| QAction *actScrollDn = menu->addAction(horiz ? tr("Scroll right") : tr("Scroll down")); |
| QAction *actionSelected = menu->exec(event->globalPos()); |
| delete menu; |
| if (actionSelected == 0) |
| /* do nothing */ ; |
| else if (actionSelected == actScrollHere) |
| setValue(d_func()->pixelPosToRangeValue(horiz ? event->pos().x() : event->pos().y())); |
| else if (actionSelected == actScrollTop) |
| triggerAction(QAbstractSlider::SliderToMinimum); |
| else if (actionSelected == actScrollBottom) |
| triggerAction(QAbstractSlider::SliderToMaximum); |
| else if (actionSelected == actPageUp) |
| triggerAction(QAbstractSlider::SliderPageStepSub); |
| else if (actionSelected == actPageDn) |
| triggerAction(QAbstractSlider::SliderPageStepAdd); |
| else if (actionSelected == actScrollUp) |
| triggerAction(QAbstractSlider::SliderSingleStepSub); |
| else if (actionSelected == actScrollDn) |
| triggerAction(QAbstractSlider::SliderSingleStepAdd); |
| #endif // QT_NO_MENU |
| } |
| #endif // QT_NO_CONTEXTMENU |
| |
| |
| /*! \reimp */ |
| QSize QScrollBar::sizeHint() const |
| { |
| ensurePolished(); |
| QStyleOptionSlider opt; |
| initStyleOption(&opt); |
| |
| int scrollBarExtent = style()->pixelMetric(QStyle::PM_ScrollBarExtent, &opt, this); |
| int scrollBarSliderMin = style()->pixelMetric(QStyle::PM_ScrollBarSliderMin, &opt, this); |
| QSize size; |
| if (opt.orientation == Qt::Horizontal) |
| size = QSize(scrollBarExtent * 2 + scrollBarSliderMin, scrollBarExtent); |
| else |
| size = QSize(scrollBarExtent, scrollBarExtent * 2 + scrollBarSliderMin); |
| |
| return style()->sizeFromContents(QStyle::CT_ScrollBar, &opt, size, this) |
| .expandedTo(QApplication::globalStrut()); |
| } |
| |
| /*!\reimp */ |
| void QScrollBar::sliderChange(SliderChange change) |
| { |
| QAbstractSlider::sliderChange(change); |
| } |
| |
| /*! |
| \reimp |
| */ |
| bool QScrollBar::event(QEvent *event) |
| { |
| switch(event->type()) { |
| case QEvent::HoverEnter: |
| case QEvent::HoverLeave: |
| case QEvent::HoverMove: |
| if (const QHoverEvent *he = static_cast<const QHoverEvent *>(event)) |
| d_func()->updateHoverControl(he->pos()); |
| break; |
| #ifndef QT_NO_WHEELEVENT |
| case QEvent::Wheel: { |
| event->ignore(); |
| // override wheel event without adding virtual function override |
| QWheelEvent *ev = static_cast<QWheelEvent *>(event); |
| int delta = ev->delta(); |
| // scrollbar is a special case - in vertical mode it reaches minimum |
| // value in the upper position, however QSlider's minimum value is on |
| // the bottom. So we need to invert a value, but since the scrollbar is |
| // inverted by default, we need to inverse the delta value for the |
| // horizontal orientation. |
| if (ev->orientation() == Qt::Horizontal) |
| delta = -delta; |
| Q_D(QScrollBar); |
| if (d->scrollByDelta(ev->orientation(), ev->modifiers(), delta)) |
| event->accept(); |
| return true; |
| } |
| #endif |
| default: |
| break; |
| } |
| return QAbstractSlider::event(event); |
| } |
| |
| /*! |
| \reimp |
| */ |
| void QScrollBar::paintEvent(QPaintEvent *) |
| { |
| Q_D(QScrollBar); |
| QPainter p(this); |
| QStyleOptionSlider opt; |
| initStyleOption(&opt); |
| opt.subControls = QStyle::SC_All; |
| if (d->pressedControl) { |
| opt.activeSubControls = (QStyle::SubControl)d->pressedControl; |
| if (!d->pointerOutsidePressedControl) |
| opt.state |= QStyle::State_Sunken; |
| } else { |
| opt.activeSubControls = (QStyle::SubControl)d->hoverControl; |
| } |
| style()->drawComplexControl(QStyle::CC_ScrollBar, &opt, &p, this); |
| } |
| |
| /*! |
| \reimp |
| */ |
| void QScrollBar::mousePressEvent(QMouseEvent *e) |
| { |
| Q_D(QScrollBar); |
| |
| if (d->repeatActionTimer.isActive()) |
| d->stopRepeatAction(); |
| |
| bool midButtonAbsPos = style()->styleHint(QStyle::SH_ScrollBar_MiddleClickAbsolutePosition, |
| 0, this); |
| QStyleOptionSlider opt; |
| initStyleOption(&opt); |
| |
| if (d->maximum == d->minimum // no range |
| || (e->buttons() & (~e->button())) // another button was clicked before |
| || !(e->button() == Qt::LeftButton || (midButtonAbsPos && e->button() == Qt::MidButton))) |
| return; |
| |
| d->pressedControl = style()->hitTestComplexControl(QStyle::CC_ScrollBar, &opt, e->pos(), this); |
| d->pointerOutsidePressedControl = false; |
| |
| QRect sr = style()->subControlRect(QStyle::CC_ScrollBar, &opt, |
| QStyle::SC_ScrollBarSlider, this); |
| QPoint click = e->pos(); |
| QPoint pressValue = click - sr.center() + sr.topLeft(); |
| d->pressValue = d->orientation == Qt::Horizontal ? d->pixelPosToRangeValue(pressValue.x()) : |
| d->pixelPosToRangeValue(pressValue.y()); |
| if (d->pressedControl == QStyle::SC_ScrollBarSlider) { |
| d->clickOffset = HORIZONTAL ? (click.x()-sr.x()) : (click.y()-sr.y()); |
| d->snapBackPosition = d->position; |
| } |
| |
| if ((d->pressedControl == QStyle::SC_ScrollBarAddPage |
| || d->pressedControl == QStyle::SC_ScrollBarSubPage) |
| && ((midButtonAbsPos && e->button() == Qt::MidButton) |
| || (style()->styleHint(QStyle::SH_ScrollBar_LeftClickAbsolutePosition, &opt, this) |
| && e->button() == Qt::LeftButton))) { |
| int sliderLength = HORIZONTAL ? sr.width() : sr.height(); |
| setSliderPosition(d->pixelPosToRangeValue((HORIZONTAL ? e->pos().x() |
| : e->pos().y()) - sliderLength / 2)); |
| d->pressedControl = QStyle::SC_ScrollBarSlider; |
| d->clickOffset = sliderLength / 2; |
| } |
| const int initialDelay = 500; // default threshold |
| d->activateControl(d->pressedControl, initialDelay); |
| QElapsedTimer time; |
| time.start(); |
| repaint(style()->subControlRect(QStyle::CC_ScrollBar, &opt, d->pressedControl, this)); |
| if (time.elapsed() >= initialDelay && d->repeatActionTimer.isActive()) { |
| // It took more than 500ms (the initial timer delay) to process the repaint(), we |
| // therefore need to restart the timer in case we have a pending mouse release event; |
| // otherwise we'll get a timer event right before the release event, |
| // causing the repeat action to be invoked twice on a single mouse click. |
| // 50ms is the default repeat time (see activateControl/setRepeatAction). |
| d->repeatActionTimer.start(50, this); |
| } |
| if (d->pressedControl == QStyle::SC_ScrollBarSlider) |
| setSliderDown(true); |
| } |
| |
| |
| /*! |
| \reimp |
| */ |
| void QScrollBar::mouseReleaseEvent(QMouseEvent *e) |
| { |
| Q_D(QScrollBar); |
| if (!d->pressedControl) |
| return; |
| |
| if (e->buttons() & (~e->button())) // some other button is still pressed |
| return; |
| |
| d->stopRepeatAction(); |
| } |
| |
| |
| /*! |
| \reimp |
| */ |
| void QScrollBar::mouseMoveEvent(QMouseEvent *e) |
| { |
| Q_D(QScrollBar); |
| if (!d->pressedControl) |
| return; |
| |
| QStyleOptionSlider opt; |
| initStyleOption(&opt); |
| if (!(e->buttons() & Qt::LeftButton |
| || ((e->buttons() & Qt::MidButton) |
| && style()->styleHint(QStyle::SH_ScrollBar_MiddleClickAbsolutePosition, &opt, this)))) |
| return; |
| |
| if (d->pressedControl == QStyle::SC_ScrollBarSlider) { |
| QPoint click = e->pos(); |
| int newPosition = d->pixelPosToRangeValue((HORIZONTAL ? click.x() : click.y()) -d->clickOffset); |
| int m = style()->pixelMetric(QStyle::PM_MaximumDragDistance, &opt, this); |
| if (m >= 0) { |
| QRect r = rect(); |
| r.adjust(-m, -m, m, m); |
| if (! r.contains(e->pos())) |
| newPosition = d->snapBackPosition; |
| } |
| setSliderPosition(newPosition); |
| } else if (!style()->styleHint(QStyle::SH_ScrollBar_ScrollWhenPointerLeavesControl, &opt, this)) { |
| |
| if (style()->styleHint(QStyle::SH_ScrollBar_RollBetweenButtons, &opt, this) |
| && d->pressedControl & (QStyle::SC_ScrollBarAddLine | QStyle::SC_ScrollBarSubLine)) { |
| QStyle::SubControl newSc = style()->hitTestComplexControl(QStyle::CC_ScrollBar, &opt, e->pos(), this); |
| if (newSc == d->pressedControl && !d->pointerOutsidePressedControl) |
| return; // nothing to do |
| if (newSc & (QStyle::SC_ScrollBarAddLine | QStyle::SC_ScrollBarSubLine)) { |
| d->pointerOutsidePressedControl = false; |
| QRect scRect = style()->subControlRect(QStyle::CC_ScrollBar, &opt, newSc, this); |
| scRect |= style()->subControlRect(QStyle::CC_ScrollBar, &opt, d->pressedControl, this); |
| d->pressedControl = newSc; |
| d->activateControl(d->pressedControl, 0); |
| update(scRect); |
| return; |
| } |
| } |
| |
| // stop scrolling when the mouse pointer leaves a control |
| // similar to push buttons |
| QRect pr = style()->subControlRect(QStyle::CC_ScrollBar, &opt, d->pressedControl, this); |
| if (pr.contains(e->pos()) == d->pointerOutsidePressedControl) { |
| if ((d->pointerOutsidePressedControl = !d->pointerOutsidePressedControl)) { |
| d->pointerOutsidePressedControl = true; |
| setRepeatAction(SliderNoAction); |
| repaint(pr); |
| } else { |
| d->activateControl(d->pressedControl); |
| } |
| } |
| } |
| } |
| |
| |
| int QScrollBarPrivate::pixelPosToRangeValue(int pos) const |
| { |
| Q_Q(const QScrollBar); |
| QStyleOptionSlider opt; |
| q->initStyleOption(&opt); |
| QRect gr = q->style()->subControlRect(QStyle::CC_ScrollBar, &opt, |
| QStyle::SC_ScrollBarGroove, q); |
| QRect sr = q->style()->subControlRect(QStyle::CC_ScrollBar, &opt, |
| QStyle::SC_ScrollBarSlider, q); |
| int sliderMin, sliderMax, sliderLength; |
| |
| if (orientation == Qt::Horizontal) { |
| sliderLength = sr.width(); |
| sliderMin = gr.x(); |
| sliderMax = gr.right() - sliderLength + 1; |
| if (q->layoutDirection() == Qt::RightToLeft) |
| opt.upsideDown = !opt.upsideDown; |
| } else { |
| sliderLength = sr.height(); |
| sliderMin = gr.y(); |
| sliderMax = gr.bottom() - sliderLength + 1; |
| } |
| |
| return QStyle::sliderValueFromPosition(minimum, maximum, pos - sliderMin, |
| sliderMax - sliderMin, opt.upsideDown); |
| } |
| |
| /*! \reimp |
| */ |
| void QScrollBar::hideEvent(QHideEvent *) |
| { |
| Q_D(QScrollBar); |
| if (d->pressedControl) { |
| d->pressedControl = QStyle::SC_None; |
| setRepeatAction(SliderNoAction); |
| } |
| } |
| |
| /*! |
| \fn bool QScrollBar::draggingSlider() |
| |
| Use isSliderDown() instead. |
| */ |
| |
| /*! \internal |
| Returns the style option for scroll bar. |
| */ |
| Q_GUI_EXPORT QStyleOptionSlider qt_qscrollbarStyleOption(QScrollBar *scrollbar) |
| { |
| QStyleOptionSlider opt; |
| scrollbar->initStyleOption(&opt); |
| return opt; |
| } |
| |
| QT_END_NAMESPACE |
| |
| #endif // QT_NO_SCROLLBAR |