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

Last change on this file since 769 was 769, checked in by Dmitry A. Kuminov, 15 years ago

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

File size: 28.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 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/qdatetime.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 // override wheel event without adding virtual function override
527 QWheelEvent *ev = static_cast<QWheelEvent *>(event);
528 int delta = ev->delta();
529 // scrollbar is a special case - in vertical mode it reaches minimum
530 // value in the upper position, however QSlider's minimum value is on
531 // the bottom. So we need to invert a value, but since the scrollbar is
532 // inverted by default, we need to inverse the delta value for the
533 // horizontal orientation.
534 if (ev->orientation() == Qt::Horizontal)
535 delta = -delta;
536 Q_D(QScrollBar);
537 if (d->scrollByDelta(ev->orientation(), ev->modifiers(), delta))
538 event->accept();
539 return true;
540 }
541#endif
542 default:
543 break;
544 }
545 return QAbstractSlider::event(event);
546}
547
548/*!
549 \reimp
550*/
551void QScrollBar::paintEvent(QPaintEvent *)
552{
553 Q_D(QScrollBar);
554 QPainter p(this);
555 QStyleOptionSlider opt;
556 initStyleOption(&opt);
557 opt.subControls = QStyle::SC_All;
558 if (d->pressedControl) {
559 opt.activeSubControls = (QStyle::SubControl)d->pressedControl;
560 if (!d->pointerOutsidePressedControl)
561 opt.state |= QStyle::State_Sunken;
562 } else {
563 opt.activeSubControls = (QStyle::SubControl)d->hoverControl;
564 }
565 style()->drawComplexControl(QStyle::CC_ScrollBar, &opt, &p, this);
566}
567
568/*!
569 \reimp
570*/
571void QScrollBar::mousePressEvent(QMouseEvent *e)
572{
573 Q_D(QScrollBar);
574
575 if (d->repeatActionTimer.isActive())
576 d->stopRepeatAction();
577
578 bool midButtonAbsPos = style()->styleHint(QStyle::SH_ScrollBar_MiddleClickAbsolutePosition,
579 0, this);
580 QStyleOptionSlider opt;
581 initStyleOption(&opt);
582
583 if (d->maximum == d->minimum // no range
584 || (e->buttons() & (~e->button())) // another button was clicked before
585 || !(e->button() == Qt::LeftButton || (midButtonAbsPos && e->button() == Qt::MidButton)))
586 return;
587
588 d->pressedControl = style()->hitTestComplexControl(QStyle::CC_ScrollBar, &opt, e->pos(), this);
589 d->pointerOutsidePressedControl = false;
590
591 QRect sr = style()->subControlRect(QStyle::CC_ScrollBar, &opt,
592 QStyle::SC_ScrollBarSlider, this);
593 QPoint click = e->pos();
594 QPoint pressValue = click - sr.center() + sr.topLeft();
595 d->pressValue = d->orientation == Qt::Horizontal ? d->pixelPosToRangeValue(pressValue.x()) :
596 d->pixelPosToRangeValue(pressValue.y());
597 if (d->pressedControl == QStyle::SC_ScrollBarSlider) {
598 d->clickOffset = HORIZONTAL ? (click.x()-sr.x()) : (click.y()-sr.y());
599 d->snapBackPosition = d->position;
600 }
601
602 if ((d->pressedControl == QStyle::SC_ScrollBarAddPage
603 || d->pressedControl == QStyle::SC_ScrollBarSubPage)
604 && ((midButtonAbsPos && e->button() == Qt::MidButton)
605 || (style()->styleHint(QStyle::SH_ScrollBar_LeftClickAbsolutePosition, &opt, this)
606 && e->button() == Qt::LeftButton))) {
607 int sliderLength = HORIZONTAL ? sr.width() : sr.height();
608 setSliderPosition(d->pixelPosToRangeValue((HORIZONTAL ? e->pos().x()
609 : e->pos().y()) - sliderLength / 2));
610 d->pressedControl = QStyle::SC_ScrollBarSlider;
611 d->clickOffset = sliderLength / 2;
612 }
613 const int initialDelay = 500; // default threshold
614 d->activateControl(d->pressedControl, initialDelay);
615 QTime time;
616 time.start();
617 repaint(style()->subControlRect(QStyle::CC_ScrollBar, &opt, d->pressedControl, this));
618 if (time.elapsed() >= initialDelay && d->repeatActionTimer.isActive()) {
619 // It took more than 500ms (the initial timer delay) to process the repaint(), we
620 // therefore need to restart the timer in case we have a pending mouse release event;
621 // otherwise we'll get a timer event right before the release event,
622 // causing the repeat action to be invoked twice on a single mouse click.
623 // 50ms is the default repeat time (see activateControl/setRepeatAction).
624 d->repeatActionTimer.start(50, this);
625 }
626 if (d->pressedControl == QStyle::SC_ScrollBarSlider)
627 setSliderDown(true);
628}
629
630
631/*!
632 \reimp
633*/
634void QScrollBar::mouseReleaseEvent(QMouseEvent *e)
635{
636 Q_D(QScrollBar);
637 if (!d->pressedControl)
638 return;
639
640 if (e->buttons() & (~e->button())) // some other button is still pressed
641 return;
642
643 d->stopRepeatAction();
644}
645
646
647/*!
648 \reimp
649*/
650void QScrollBar::mouseMoveEvent(QMouseEvent *e)
651{
652 Q_D(QScrollBar);
653 if (!d->pressedControl)
654 return;
655
656 QStyleOptionSlider opt;
657 initStyleOption(&opt);
658 if (!(e->buttons() & Qt::LeftButton
659 || ((e->buttons() & Qt::MidButton)
660 && style()->styleHint(QStyle::SH_ScrollBar_MiddleClickAbsolutePosition, &opt, this))))
661 return;
662
663 if (d->pressedControl == QStyle::SC_ScrollBarSlider) {
664 QPoint click = e->pos();
665 int newPosition = d->pixelPosToRangeValue((HORIZONTAL ? click.x() : click.y()) -d->clickOffset);
666 int m = style()->pixelMetric(QStyle::PM_MaximumDragDistance, &opt, this);
667 if (m >= 0) {
668 QRect r = rect();
669 r.adjust(-m, -m, m, m);
670 if (! r.contains(e->pos()))
671 newPosition = d->snapBackPosition;
672 }
673 setSliderPosition(newPosition);
674 } else if (!style()->styleHint(QStyle::SH_ScrollBar_ScrollWhenPointerLeavesControl, &opt, this)) {
675
676 if (style()->styleHint(QStyle::SH_ScrollBar_RollBetweenButtons, &opt, this)
677 && d->pressedControl & (QStyle::SC_ScrollBarAddLine | QStyle::SC_ScrollBarSubLine)) {
678 QStyle::SubControl newSc = style()->hitTestComplexControl(QStyle::CC_ScrollBar, &opt, e->pos(), this);
679 if (newSc == d->pressedControl && !d->pointerOutsidePressedControl)
680 return; // nothing to do
681 if (newSc & (QStyle::SC_ScrollBarAddLine | QStyle::SC_ScrollBarSubLine)) {
682 d->pointerOutsidePressedControl = false;
683 QRect scRect = style()->subControlRect(QStyle::CC_ScrollBar, &opt, newSc, this);
684 scRect |= style()->subControlRect(QStyle::CC_ScrollBar, &opt, d->pressedControl, this);
685 d->pressedControl = newSc;
686 d->activateControl(d->pressedControl, 0);
687 update(scRect);
688 return;
689 }
690 }
691
692 // stop scrolling when the mouse pointer leaves a control
693 // similar to push buttons
694 QRect pr = style()->subControlRect(QStyle::CC_ScrollBar, &opt, d->pressedControl, this);
695 if (pr.contains(e->pos()) == d->pointerOutsidePressedControl) {
696 if ((d->pointerOutsidePressedControl = !d->pointerOutsidePressedControl)) {
697 d->pointerOutsidePressedControl = true;
698 setRepeatAction(SliderNoAction);
699 repaint(pr);
700 } else {
701 d->activateControl(d->pressedControl);
702 }
703 }
704 }
705}
706
707
708int QScrollBarPrivate::pixelPosToRangeValue(int pos) const
709{
710 Q_Q(const QScrollBar);
711 QStyleOptionSlider opt;
712 q->initStyleOption(&opt);
713 QRect gr = q->style()->subControlRect(QStyle::CC_ScrollBar, &opt,
714 QStyle::SC_ScrollBarGroove, q);
715 QRect sr = q->style()->subControlRect(QStyle::CC_ScrollBar, &opt,
716 QStyle::SC_ScrollBarSlider, q);
717 int sliderMin, sliderMax, sliderLength;
718
719 if (orientation == Qt::Horizontal) {
720 sliderLength = sr.width();
721 sliderMin = gr.x();
722 sliderMax = gr.right() - sliderLength + 1;
723 if (q->layoutDirection() == Qt::RightToLeft)
724 opt.upsideDown = !opt.upsideDown;
725 } else {
726 sliderLength = sr.height();
727 sliderMin = gr.y();
728 sliderMax = gr.bottom() - sliderLength + 1;
729 }
730
731 return QStyle::sliderValueFromPosition(minimum, maximum, pos - sliderMin,
732 sliderMax - sliderMin, opt.upsideDown);
733}
734
735/*! \reimp
736*/
737void QScrollBar::hideEvent(QHideEvent *)
738{
739 Q_D(QScrollBar);
740 if (d->pressedControl) {
741 d->pressedControl = QStyle::SC_None;
742 setRepeatAction(SliderNoAction);
743 }
744}
745
746/*!
747 \fn bool QScrollBar::draggingSlider()
748
749 Use isSliderDown() instead.
750*/
751
752/*! \internal
753 Returns the style option for scroll bar.
754*/
755Q_GUI_EXPORT QStyleOptionSlider qt_qscrollbarStyleOption(QScrollBar *scrollbar)
756{
757 QStyleOptionSlider opt;
758 scrollbar->initStyleOption(&opt);
759 return opt;
760}
761
762QT_END_NAMESPACE
763
764#endif // QT_NO_SCROLLBAR
Note: See TracBrowser for help on using the repository browser.