source: trunk/src/gui/widgets/qscrollbar.cpp@ 1021

Last change on this file since 1021 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 28.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qapplication.h"
43#include "qcursor.h"
44#include "qevent.h"
45#include "qpainter.h"
46#include "qscrollbar.h"
47#include "qstyle.h"
48#include "qstyleoption.h"
49#include "qmenu.h"
50#include <QtCore/qelapsedtimer.h>
51
52#ifndef QT_NO_SCROLLBAR
53
54#ifndef QT_NO_ACCESSIBILITY
55#include "qaccessible.h"
56#endif
57#include <limits.h>
58#include "qabstractslider_p.h"
59
60QT_BEGIN_NAMESPACE
61
62/*!
63 \class QScrollBar
64 \brief The QScrollBar widget provides a vertical or horizontal scroll bar.
65
66 \ingroup basicwidgets
67
68 A scroll bar is a control that enables the user to access parts of a
69 document that is larger than the widget used to display it. It provides
70 a visual indication of the user's current position within the document
71 and the amount of the document that is visible. Scroll bars are usually
72 equipped with other controls that enable more accurate navigation.
73 Qt displays scroll bars in a way that is appropriate for each platform.
74
75 If you need to provide a scrolling view onto another widget, it may be
76 more convenient to use the QScrollArea class because this provides a
77 viewport widget and scroll bars. QScrollBar is useful if you need to
78 implement similar functionality for specialized widgets using QAbstractScrollArea;
79 for example, if you decide to subclass QAbstractItemView.
80 For most other situations where a slider control is used to obtain a value
81 within a given range, the QSlider class may be more appropriate for your
82 needs.
83
84 \table
85 \row \i \image qscrollbar-picture.png
86 \i Scroll bars typically include four separate controls: a slider,
87 scroll arrows, and a page control.
88
89 \list
90 \i a. The slider provides a way to quickly go to any part of the
91 document, but does not support accurate navigation within large
92 documents.
93 \i b. The scroll arrows are push buttons which can be used to accurately
94 navigate to a particular place in a document. For a vertical scroll bar
95 connected to a text editor, these typically move the current position one
96 "line" up or down, and adjust the position of the slider by a small
97 amount. In editors and list boxes a "line" might mean one line of text;
98 in an image viewer it might mean 20 pixels.
99 \i c. The page control is the area over which the slider is dragged (the
100 scroll bar's background). Clicking here moves the scroll bar towards
101 the click by one "page". This value is usually the same as the length of
102 the slider.
103 \endlist
104 \endtable
105
106 Each scroll bar has a value that indicates how far the slider is from
107 the start of the scroll bar; this is obtained with value() and set
108 with setValue(). This value always lies within the range of values
109 defined for the scroll bar, from \l{QAbstractSlider::minimum()}{minimum()}
110 to \l{QAbstractSlider::minimum()}{maximum()} inclusive. The range of
111 acceptable values can be set with setMinimum() and setMaximum().
112 At the minimum value, the top edge of the slider (for a vertical scroll
113 bar) or left edge (for a horizontal scroll bar) will be at the top (or
114 left) end of the scroll bar. At the maximum value, the bottom (or right)
115 edge of the slider will be at the bottom (or right) end of the scroll bar.
116
117 The length of the slider is usually related to the value of the page step,
118 and typically represents the proportion of the document area shown in a
119 scrolling view. The page step is the amount that the value changes by
120 when the user presses the \key{Page Up} and \key{Page Down} keys, and is
121 set with setPageStep(). Smaller changes to the value defined by the
122 line step are made using the cursor keys, and this quantity is set with
123 \l{QAbstractSlider::}{setSingleStep()}.
124
125 Note that the range of values used is independent of the actual size
126 of the scroll bar widget. You do not need to take this into account when
127 you choose values for the range and the page step.
128
129 The range of values specified for the scroll bar are often determined
130 differently to those for a QSlider because the length of the slider
131 needs to be taken into account. If we have a document with 100 lines,
132 and we can only show 20 lines in a widget, we may wish to construct a
133 scroll bar with a page step of 20, a minimum value of 0, and a maximum
134 value of 80. This would give us a scroll bar with five "pages".
135
136 \table
137 \row \i \inlineimage qscrollbar-values.png
138 \i The relationship between a document length, the range of values used
139 in a scroll bar, and the page step is simple in many common situations.
140 The scroll bar's range of values is determined by subtracting a
141 chosen page step from some value representing the length of the document.
142 In such cases, the following equation is useful:
143
144 \e{document length} = maximum() - minimum() + pageStep().
145 \endtable
146
147 QScrollBar only provides integer ranges. Note that although
148 QScrollBar handles very large numbers, scroll bars on current
149 screens cannot usefully represent ranges above about 100,000 pixels.
150 Beyond that, it becomes difficult for the user to control the
151 slider using either the keyboard or the mouse, and the scroll
152 arrows will have limited use.
153
154 ScrollBar inherits a comprehensive set of signals from QAbstractSlider:
155 \list
156 \i \l{QAbstractSlider::valueChanged()}{valueChanged()} is emitted when the
157 scroll bar's value has changed. The tracking() determines whether this
158 signal is emitted during user interaction.
159 \i \l{QAbstractSlider::rangeChanged()}{rangeChanged()} is emitted when the
160 scroll bar's range of values has changed.
161 \i \l{QAbstractSlider::sliderPressed()}{sliderPressed()} is emitted when
162 the user starts to drag the slider.
163 \i \l{QAbstractSlider::sliderMoved()}{sliderMoved()} is emitted when the user
164 drags the slider.
165 \i \l{QAbstractSlider::sliderReleased()}{sliderReleased()} is emitted when
166 the user releases the slider.
167 \i \l{QAbstractSlider::actionTriggered()}{actionTriggered()} is emitted
168 when the scroll bar is changed by user interaction or via the
169 \l{QAbstractSlider::triggerAction()}{triggerAction()} function.
170 \endlist
171
172 A scroll bar can be controlled by the keyboard, but it has a
173 default focusPolicy() of Qt::NoFocus. Use setFocusPolicy() to
174 enable keyboard interaction with the scroll bar:
175 \list
176 \i Left/Right move a horizontal scroll bar by one single step.
177 \i Up/Down move a vertical scroll bar by one single step.
178 \i PageUp moves up one page.
179 \i PageDown moves down one page.
180 \i Home moves to the start (mininum).
181 \i End moves to the end (maximum).
182 \endlist
183
184 The slider itself can be controlled by using the
185 \l{QAbstractSlider::triggerAction()}{triggerAction()} function to simulate
186 user interaction with the scroll bar controls. This is useful if you have
187 many different widgets that use a common range of values.
188
189 Most GUI styles use the pageStep() value to calculate the size of the
190 slider.
191
192 \table 100%
193 \row \o \inlineimage macintosh-horizontalscrollbar.png Screenshot of a Macintosh style scroll bar
194 \o A scroll bar shown in the \l{Macintosh Style Widget Gallery}{Macintosh widget style}.
195 \row \o \inlineimage windowsxp-horizontalscrollbar.png Screenshot of a Windows XP style scroll bar
196 \o A scroll bar shown in the \l{Windows XP Style Widget Gallery}{Windows XP widget style}.
197 \row \o \inlineimage plastique-horizontalscrollbar.png Screenshot of a Plastique style scroll bar
198 \o A scroll bar shown in the \l{Plastique Style Widget Gallery}{Plastique widget style}.
199 \endtable
200
201 \sa QScrollArea, QSlider, QDial, QSpinBox, {fowler}{GUI Design Handbook: Scroll Bar}, {Sliders Example}
202*/
203
204class QScrollBarPrivate : public QAbstractSliderPrivate
205{
206 Q_DECLARE_PUBLIC(QScrollBar)
207public:
208 QStyle::SubControl pressedControl;
209 bool pointerOutsidePressedControl;
210
211 int clickOffset, snapBackPosition;
212
213 void activateControl(uint control, int threshold = 500);
214 void stopRepeatAction();
215 int pixelPosToRangeValue(int pos) const;
216 void init();
217 bool updateHoverControl(const QPoint &pos);
218 QStyle::SubControl newHoverControl(const QPoint &pos);
219
220 QStyle::SubControl hoverControl;
221 QRect hoverRect;
222};
223
224bool QScrollBarPrivate::updateHoverControl(const QPoint &pos)
225{
226 Q_Q(QScrollBar);
227 QRect lastHoverRect = hoverRect;
228 QStyle::SubControl lastHoverControl = hoverControl;
229 bool doesHover = q->testAttribute(Qt::WA_Hover);
230 if (lastHoverControl != newHoverControl(pos) && doesHover) {
231 q->update(lastHoverRect);
232 q->update(hoverRect);
233 return true;
234 }
235 return !doesHover;
236}
237
238QStyle::SubControl QScrollBarPrivate::newHoverControl(const QPoint &pos)
239{
240 Q_Q(QScrollBar);
241 QStyleOptionSlider opt;
242 q->initStyleOption(&opt);
243 opt.subControls = QStyle::SC_All;
244 hoverControl = q->style()->hitTestComplexControl(QStyle::CC_ScrollBar, &opt, pos, q);
245 if (hoverControl == QStyle::SC_None)
246 hoverRect = QRect();
247 else
248 hoverRect = q->style()->subControlRect(QStyle::CC_ScrollBar, &opt, hoverControl, q);
249 return hoverControl;
250}
251
252void QScrollBarPrivate::activateControl(uint control, int threshold)
253{
254 QAbstractSlider::SliderAction action = QAbstractSlider::SliderNoAction;
255 switch (control) {
256 case QStyle::SC_ScrollBarAddPage:
257 action = QAbstractSlider::SliderPageStepAdd;
258 break;
259 case QStyle::SC_ScrollBarSubPage:
260 action = QAbstractSlider::SliderPageStepSub;
261 break;
262 case QStyle::SC_ScrollBarAddLine:
263 action = QAbstractSlider::SliderSingleStepAdd;
264 break;
265 case QStyle::SC_ScrollBarSubLine:
266 action = QAbstractSlider::SliderSingleStepSub;
267 break;
268 case QStyle::SC_ScrollBarFirst:
269 action = QAbstractSlider::SliderToMinimum;
270 break;
271 case QStyle::SC_ScrollBarLast:
272 action = QAbstractSlider::SliderToMaximum;
273 break;
274 default:
275 break;
276 }
277
278 if (action) {
279 q_func()->setRepeatAction(action, threshold);
280 q_func()->triggerAction(action);
281 }
282}
283
284void QScrollBarPrivate::stopRepeatAction()
285{
286 Q_Q(QScrollBar);
287 QStyle::SubControl tmp = pressedControl;
288 q->setRepeatAction(QAbstractSlider::SliderNoAction);
289 pressedControl = QStyle::SC_None;
290
291 if (tmp == QStyle::SC_ScrollBarSlider)
292 q->setSliderDown(false);
293
294 QStyleOptionSlider opt;
295 q->initStyleOption(&opt);
296 q->repaint(q->style()->subControlRect(QStyle::CC_ScrollBar, &opt, tmp, q));
297}
298
299/*!
300 Initialize \a option with the values from this QScrollBar. This method
301 is useful for subclasses when they need a QStyleOptionSlider, but don't want
302 to fill in all the information themselves.
303
304 \sa QStyleOption::initFrom()
305*/
306void QScrollBar::initStyleOption(QStyleOptionSlider *option) const
307{
308 if (!option)
309 return;
310
311 Q_D(const QScrollBar);
312 option->initFrom(this);
313 option->subControls = QStyle::SC_None;
314 option->activeSubControls = QStyle::SC_None;
315 option->orientation = d->orientation;
316 option->minimum = d->minimum;
317 option->maximum = d->maximum;
318 option->sliderPosition = d->position;
319 option->sliderValue = d->value;
320 option->singleStep = d->singleStep;
321 option->pageStep = d->pageStep;
322 option->upsideDown = d->invertedAppearance;
323 if (d->orientation == Qt::Horizontal)
324 option->state |= QStyle::State_Horizontal;
325}
326
327
328#define HORIZONTAL (d_func()->orientation == Qt::Horizontal)
329#define VERTICAL !HORIZONTAL
330
331/*!
332 Constructs a vertical scroll bar.
333
334 The \a parent arguments is sent to the QWidget constructor.
335
336 The \l {QAbstractSlider::minimum} {minimum} defaults to 0, the
337 \l {QAbstractSlider::maximum} {maximum} to 99, with a
338 \l {QAbstractSlider::singleStep} {singleStep} size of 1 and a
339 \l {QAbstractSlider::pageStep} {pageStep} size of 10, and an
340 initial \l {QAbstractSlider::value} {value} of 0.
341*/
342QScrollBar::QScrollBar(QWidget *parent)
343 : QAbstractSlider(*new QScrollBarPrivate, parent)
344{
345 d_func()->orientation = Qt::Vertical;
346 d_func()->init();
347}
348
349/*!
350 Constructs a scroll bar with the given \a orientation.
351
352 The \a parent argument is passed to the QWidget constructor.
353
354 The \l {QAbstractSlider::minimum} {minimum} defaults to 0, the
355 \l {QAbstractSlider::maximum} {maximum} to 99, with a
356 \l {QAbstractSlider::singleStep} {singleStep} size of 1 and a
357 \l {QAbstractSlider::pageStep} {pageStep} size of 10, and an
358 initial \l {QAbstractSlider::value} {value} of 0.
359*/
360QScrollBar::QScrollBar(Qt::Orientation orientation, QWidget *parent)
361 : QAbstractSlider(*new QScrollBarPrivate, parent)
362{
363 d_func()->orientation = orientation;
364 d_func()->init();
365}
366
367
368#ifdef QT3_SUPPORT
369/*!
370 Use one of the constructors that doesn't take the \a name
371 argument and then use setObjectName() instead.
372*/
373QScrollBar::QScrollBar(QWidget *parent, const char *name)
374 : QAbstractSlider(*new QScrollBarPrivate, parent)
375{
376 setObjectName(QString::fromAscii(name));
377 d_func()->orientation = Qt::Vertical;
378 d_func()->init();
379}
380
381/*!
382 Use one of the constructors that doesn't take the \a name
383 argument and then use setObjectName() instead.
384*/
385QScrollBar::QScrollBar(Qt::Orientation orientation, QWidget *parent, const char *name)
386 : QAbstractSlider(*new QScrollBarPrivate, parent)
387{
388 setObjectName(QString::fromAscii(name));
389 d_func()->orientation = orientation;
390 d_func()->init();
391}
392
393/*!
394 Use one of the constructors that doesn't take the \a name
395 argument and then use setObjectName() instead.
396*/
397QScrollBar::QScrollBar(int minimum, int maximum, int lineStep, int pageStep,
398 int value, Qt::Orientation orientation,
399 QWidget *parent, const char *name)
400 : QAbstractSlider(*new QScrollBarPrivate, parent)
401{
402 Q_D(QScrollBar);
403 setObjectName(QString::fromAscii(name));
404 d->minimum = minimum;
405 d->maximum = maximum;
406 d->singleStep = lineStep;
407 d->pageStep = pageStep;
408 d->value = value;
409 d->orientation = orientation;
410 d->init();
411}
412#endif // QT3_SUPPORT
413
414/*!
415 Destroys the scroll bar.
416*/
417QScrollBar::~QScrollBar()
418{
419}
420
421void QScrollBarPrivate::init()
422{
423 Q_Q(QScrollBar);
424 invertedControls = true;
425 pressedControl = hoverControl = QStyle::SC_None;
426 pointerOutsidePressedControl = false;
427 q->setFocusPolicy(Qt::NoFocus);
428 QSizePolicy sp(QSizePolicy::Minimum, QSizePolicy::Fixed, QSizePolicy::Slider);
429 if (orientation == Qt::Vertical)
430 sp.transpose();
431 q->setSizePolicy(sp);
432 q->setAttribute(Qt::WA_WState_OwnSizePolicy, false);
433 q->setAttribute(Qt::WA_OpaquePaintEvent);
434
435#if !defined(QT_NO_CONTEXTMENU) && defined(Q_WS_WINCE)
436 if (!q->style()->styleHint(QStyle::SH_ScrollBar_ContextMenu, 0, q)) {
437 q->setContextMenuPolicy(Qt::PreventContextMenu);
438 }
439#endif
440}
441
442#ifndef QT_NO_CONTEXTMENU
443/*! \reimp */
444void QScrollBar::contextMenuEvent(QContextMenuEvent *event)
445{
446 if (!style()->styleHint(QStyle::SH_ScrollBar_ContextMenu, 0, this)) {
447 QAbstractSlider::contextMenuEvent(event);
448 return ;
449 }
450
451#ifndef QT_NO_MENU
452 bool horiz = HORIZONTAL;
453 QPointer<QMenu> menu = new QMenu(this);
454 QAction *actScrollHere = menu->addAction(tr("Scroll here"));
455 menu->addSeparator();
456 QAction *actScrollTop = menu->addAction(horiz ? tr("Left edge") : tr("Top"));
457 QAction *actScrollBottom = menu->addAction(horiz ? tr("Right edge") : tr("Bottom"));
458 menu->addSeparator();
459 QAction *actPageUp = menu->addAction(horiz ? tr("Page left") : tr("Page up"));
460 QAction *actPageDn = menu->addAction(horiz ? tr("Page right") : tr("Page down"));
461 menu->addSeparator();
462 QAction *actScrollUp = menu->addAction(horiz ? tr("Scroll left") : tr("Scroll up"));
463 QAction *actScrollDn = menu->addAction(horiz ? tr("Scroll right") : tr("Scroll down"));
464 QAction *actionSelected = menu->exec(event->globalPos());
465 delete menu;
466 if (actionSelected == 0)
467 /* do nothing */ ;
468 else if (actionSelected == actScrollHere)
469 setValue(d_func()->pixelPosToRangeValue(horiz ? event->pos().x() : event->pos().y()));
470 else if (actionSelected == actScrollTop)
471 triggerAction(QAbstractSlider::SliderToMinimum);
472 else if (actionSelected == actScrollBottom)
473 triggerAction(QAbstractSlider::SliderToMaximum);
474 else if (actionSelected == actPageUp)
475 triggerAction(QAbstractSlider::SliderPageStepSub);
476 else if (actionSelected == actPageDn)
477 triggerAction(QAbstractSlider::SliderPageStepAdd);
478 else if (actionSelected == actScrollUp)
479 triggerAction(QAbstractSlider::SliderSingleStepSub);
480 else if (actionSelected == actScrollDn)
481 triggerAction(QAbstractSlider::SliderSingleStepAdd);
482#endif // QT_NO_MENU
483}
484#endif // QT_NO_CONTEXTMENU
485
486
487/*! \reimp */
488QSize QScrollBar::sizeHint() const
489{
490 ensurePolished();
491 QStyleOptionSlider opt;
492 initStyleOption(&opt);
493
494 int scrollBarExtent = style()->pixelMetric(QStyle::PM_ScrollBarExtent, &opt, this);
495 int scrollBarSliderMin = style()->pixelMetric(QStyle::PM_ScrollBarSliderMin, &opt, this);
496 QSize size;
497 if (opt.orientation == Qt::Horizontal)
498 size = QSize(scrollBarExtent * 2 + scrollBarSliderMin, scrollBarExtent);
499 else
500 size = QSize(scrollBarExtent, scrollBarExtent * 2 + scrollBarSliderMin);
501
502 return style()->sizeFromContents(QStyle::CT_ScrollBar, &opt, size, this)
503 .expandedTo(QApplication::globalStrut());
504 }
505
506/*!\reimp */
507void QScrollBar::sliderChange(SliderChange change)
508{
509 QAbstractSlider::sliderChange(change);
510}
511
512/*!
513 \reimp
514*/
515bool QScrollBar::event(QEvent *event)
516{
517 switch(event->type()) {
518 case QEvent::HoverEnter:
519 case QEvent::HoverLeave:
520 case QEvent::HoverMove:
521 if (const QHoverEvent *he = static_cast<const QHoverEvent *>(event))
522 d_func()->updateHoverControl(he->pos());
523 break;
524#ifndef QT_NO_WHEELEVENT
525 case QEvent::Wheel: {
526 event->ignore();
527 // override wheel event without adding virtual function override
528 QWheelEvent *ev = static_cast<QWheelEvent *>(event);
529 int delta = ev->delta();
530 // scrollbar is a special case - in vertical mode it reaches minimum
531 // value in the upper position, however QSlider's minimum value is on
532 // the bottom. So we need to invert a value, but since the scrollbar is
533 // inverted by default, we need to inverse the delta value for the
534 // horizontal orientation.
535 if (ev->orientation() == Qt::Horizontal)
536 delta = -delta;
537 Q_D(QScrollBar);
538 if (d->scrollByDelta(ev->orientation(), ev->modifiers(), delta))
539 event->accept();
540 return true;
541 }
542#endif
543 default:
544 break;
545 }
546 return QAbstractSlider::event(event);
547}
548
549/*!
550 \reimp
551*/
552void QScrollBar::paintEvent(QPaintEvent *)
553{
554 Q_D(QScrollBar);
555 QPainter p(this);
556 QStyleOptionSlider opt;
557 initStyleOption(&opt);
558 opt.subControls = QStyle::SC_All;
559 if (d->pressedControl) {
560 opt.activeSubControls = (QStyle::SubControl)d->pressedControl;
561 if (!d->pointerOutsidePressedControl)
562 opt.state |= QStyle::State_Sunken;
563 } else {
564 opt.activeSubControls = (QStyle::SubControl)d->hoverControl;
565 }
566 style()->drawComplexControl(QStyle::CC_ScrollBar, &opt, &p, this);
567}
568
569/*!
570 \reimp
571*/
572void QScrollBar::mousePressEvent(QMouseEvent *e)
573{
574 Q_D(QScrollBar);
575
576 if (d->repeatActionTimer.isActive())
577 d->stopRepeatAction();
578
579 bool midButtonAbsPos = style()->styleHint(QStyle::SH_ScrollBar_MiddleClickAbsolutePosition,
580 0, this);
581 QStyleOptionSlider opt;
582 initStyleOption(&opt);
583
584 if (d->maximum == d->minimum // no range
585 || (e->buttons() & (~e->button())) // another button was clicked before
586 || !(e->button() == Qt::LeftButton || (midButtonAbsPos && e->button() == Qt::MidButton)))
587 return;
588
589 d->pressedControl = style()->hitTestComplexControl(QStyle::CC_ScrollBar, &opt, e->pos(), this);
590 d->pointerOutsidePressedControl = false;
591
592 QRect sr = style()->subControlRect(QStyle::CC_ScrollBar, &opt,
593 QStyle::SC_ScrollBarSlider, this);
594 QPoint click = e->pos();
595 QPoint pressValue = click - sr.center() + sr.topLeft();
596 d->pressValue = d->orientation == Qt::Horizontal ? d->pixelPosToRangeValue(pressValue.x()) :
597 d->pixelPosToRangeValue(pressValue.y());
598 if (d->pressedControl == QStyle::SC_ScrollBarSlider) {
599 d->clickOffset = HORIZONTAL ? (click.x()-sr.x()) : (click.y()-sr.y());
600 d->snapBackPosition = d->position;
601 }
602
603 if ((d->pressedControl == QStyle::SC_ScrollBarAddPage
604 || d->pressedControl == QStyle::SC_ScrollBarSubPage)
605 && ((midButtonAbsPos && e->button() == Qt::MidButton)
606 || (style()->styleHint(QStyle::SH_ScrollBar_LeftClickAbsolutePosition, &opt, this)
607 && e->button() == Qt::LeftButton))) {
608 int sliderLength = HORIZONTAL ? sr.width() : sr.height();
609 setSliderPosition(d->pixelPosToRangeValue((HORIZONTAL ? e->pos().x()
610 : e->pos().y()) - sliderLength / 2));
611 d->pressedControl = QStyle::SC_ScrollBarSlider;
612 d->clickOffset = sliderLength / 2;
613 }
614 const int initialDelay = 500; // default threshold
615 d->activateControl(d->pressedControl, initialDelay);
616 QElapsedTimer time;
617 time.start();
618 repaint(style()->subControlRect(QStyle::CC_ScrollBar, &opt, d->pressedControl, this));
619 if (time.elapsed() >= initialDelay && d->repeatActionTimer.isActive()) {
620 // It took more than 500ms (the initial timer delay) to process the repaint(), we
621 // therefore need to restart the timer in case we have a pending mouse release event;
622 // otherwise we'll get a timer event right before the release event,
623 // causing the repeat action to be invoked twice on a single mouse click.
624 // 50ms is the default repeat time (see activateControl/setRepeatAction).
625 d->repeatActionTimer.start(50, this);
626 }
627 if (d->pressedControl == QStyle::SC_ScrollBarSlider)
628 setSliderDown(true);
629}
630
631
632/*!
633 \reimp
634*/
635void QScrollBar::mouseReleaseEvent(QMouseEvent *e)
636{
637 Q_D(QScrollBar);
638 if (!d->pressedControl)
639 return;
640
641 if (e->buttons() & (~e->button())) // some other button is still pressed
642 return;
643
644 d->stopRepeatAction();
645}
646
647
648/*!
649 \reimp
650*/
651void QScrollBar::mouseMoveEvent(QMouseEvent *e)
652{
653 Q_D(QScrollBar);
654 if (!d->pressedControl)
655 return;
656
657 QStyleOptionSlider opt;
658 initStyleOption(&opt);
659 if (!(e->buttons() & Qt::LeftButton
660 || ((e->buttons() & Qt::MidButton)
661 && style()->styleHint(QStyle::SH_ScrollBar_MiddleClickAbsolutePosition, &opt, this))))
662 return;
663
664 if (d->pressedControl == QStyle::SC_ScrollBarSlider) {
665 QPoint click = e->pos();
666 int newPosition = d->pixelPosToRangeValue((HORIZONTAL ? click.x() : click.y()) -d->clickOffset);
667 int m = style()->pixelMetric(QStyle::PM_MaximumDragDistance, &opt, this);
668 if (m >= 0) {
669 QRect r = rect();
670 r.adjust(-m, -m, m, m);
671 if (! r.contains(e->pos()))
672 newPosition = d->snapBackPosition;
673 }
674 setSliderPosition(newPosition);
675 } else if (!style()->styleHint(QStyle::SH_ScrollBar_ScrollWhenPointerLeavesControl, &opt, this)) {
676
677 if (style()->styleHint(QStyle::SH_ScrollBar_RollBetweenButtons, &opt, this)
678 && d->pressedControl & (QStyle::SC_ScrollBarAddLine | QStyle::SC_ScrollBarSubLine)) {
679 QStyle::SubControl newSc = style()->hitTestComplexControl(QStyle::CC_ScrollBar, &opt, e->pos(), this);
680 if (newSc == d->pressedControl && !d->pointerOutsidePressedControl)
681 return; // nothing to do
682 if (newSc & (QStyle::SC_ScrollBarAddLine | QStyle::SC_ScrollBarSubLine)) {
683 d->pointerOutsidePressedControl = false;
684 QRect scRect = style()->subControlRect(QStyle::CC_ScrollBar, &opt, newSc, this);
685 scRect |= style()->subControlRect(QStyle::CC_ScrollBar, &opt, d->pressedControl, this);
686 d->pressedControl = newSc;
687 d->activateControl(d->pressedControl, 0);
688 update(scRect);
689 return;
690 }
691 }
692
693 // stop scrolling when the mouse pointer leaves a control
694 // similar to push buttons
695 QRect pr = style()->subControlRect(QStyle::CC_ScrollBar, &opt, d->pressedControl, this);
696 if (pr.contains(e->pos()) == d->pointerOutsidePressedControl) {
697 if ((d->pointerOutsidePressedControl = !d->pointerOutsidePressedControl)) {
698 d->pointerOutsidePressedControl = true;
699 setRepeatAction(SliderNoAction);
700 repaint(pr);
701 } else {
702 d->activateControl(d->pressedControl);
703 }
704 }
705 }
706}
707
708
709int QScrollBarPrivate::pixelPosToRangeValue(int pos) const
710{
711 Q_Q(const QScrollBar);
712 QStyleOptionSlider opt;
713 q->initStyleOption(&opt);
714 QRect gr = q->style()->subControlRect(QStyle::CC_ScrollBar, &opt,
715 QStyle::SC_ScrollBarGroove, q);
716 QRect sr = q->style()->subControlRect(QStyle::CC_ScrollBar, &opt,
717 QStyle::SC_ScrollBarSlider, q);
718 int sliderMin, sliderMax, sliderLength;
719
720 if (orientation == Qt::Horizontal) {
721 sliderLength = sr.width();
722 sliderMin = gr.x();
723 sliderMax = gr.right() - sliderLength + 1;
724 if (q->layoutDirection() == Qt::RightToLeft)
725 opt.upsideDown = !opt.upsideDown;
726 } else {
727 sliderLength = sr.height();
728 sliderMin = gr.y();
729 sliderMax = gr.bottom() - sliderLength + 1;
730 }
731
732 return QStyle::sliderValueFromPosition(minimum, maximum, pos - sliderMin,
733 sliderMax - sliderMin, opt.upsideDown);
734}
735
736/*! \reimp
737*/
738void QScrollBar::hideEvent(QHideEvent *)
739{
740 Q_D(QScrollBar);
741 if (d->pressedControl) {
742 d->pressedControl = QStyle::SC_None;
743 setRepeatAction(SliderNoAction);
744 }
745}
746
747/*!
748 \fn bool QScrollBar::draggingSlider()
749
750 Use isSliderDown() instead.
751*/
752
753/*! \internal
754 Returns the style option for scroll bar.
755*/
756Q_GUI_EXPORT QStyleOptionSlider qt_qscrollbarStyleOption(QScrollBar *scrollbar)
757{
758 QStyleOptionSlider opt;
759 scrollbar->initStyleOption(&opt);
760 return opt;
761}
762
763QT_END_NAMESPACE
764
765#endif // QT_NO_SCROLLBAR
Note: See TracBrowser for help on using the repository browser.