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

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

trunk: Merged in qt 4.6.1 sources.

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