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

Last change on this file since 64 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 27.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** 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 are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
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
436#ifndef QT_NO_CONTEXTMENU
437/*! \reimp */
438void QScrollBar::contextMenuEvent(QContextMenuEvent *event)
439{
440 if (!style()->styleHint(QStyle::SH_ScrollBar_ContextMenu, 0, this)) {
441 QAbstractSlider::contextMenuEvent(event);
442 return ;
443 }
444
445#ifndef QT_NO_MENU
446 bool horiz = HORIZONTAL;
447 QPointer<QMenu> menu = new QMenu(this);
448 QAction *actScrollHere = menu->addAction(tr("Scroll here"));
449 menu->addSeparator();
450 QAction *actScrollTop = menu->addAction(horiz ? tr("Left edge") : tr("Top"));
451 QAction *actScrollBottom = menu->addAction(horiz ? tr("Right edge") : tr("Bottom"));
452 menu->addSeparator();
453 QAction *actPageUp = menu->addAction(horiz ? tr("Page left") : tr("Page up"));
454 QAction *actPageDn = menu->addAction(horiz ? tr("Page right") : tr("Page down"));
455 menu->addSeparator();
456 QAction *actScrollUp = menu->addAction(horiz ? tr("Scroll left") : tr("Scroll up"));
457 QAction *actScrollDn = menu->addAction(horiz ? tr("Scroll right") : tr("Scroll down"));
458 QAction *actionSelected = menu->exec(event->globalPos());
459 delete menu;
460 if (actionSelected == 0)
461 /* do nothing */ ;
462 else if (actionSelected == actScrollHere)
463 setValue(d_func()->pixelPosToRangeValue(horiz ? event->pos().x() : event->pos().y()));
464 else if (actionSelected == actScrollTop)
465 triggerAction(QAbstractSlider::SliderToMinimum);
466 else if (actionSelected == actScrollBottom)
467 triggerAction(QAbstractSlider::SliderToMaximum);
468 else if (actionSelected == actPageUp)
469 triggerAction(QAbstractSlider::SliderPageStepSub);
470 else if (actionSelected == actPageDn)
471 triggerAction(QAbstractSlider::SliderPageStepAdd);
472 else if (actionSelected == actScrollUp)
473 triggerAction(QAbstractSlider::SliderSingleStepSub);
474 else if (actionSelected == actScrollDn)
475 triggerAction(QAbstractSlider::SliderSingleStepAdd);
476#endif // QT_NO_MENU
477}
478#endif // QT_NO_CONTEXTMENU
479
480
481/*! \reimp */
482QSize QScrollBar::sizeHint() const
483{
484 ensurePolished();
485 QStyleOptionSlider opt;
486 initStyleOption(&opt);
487
488 int scrollBarExtent = style()->pixelMetric(QStyle::PM_ScrollBarExtent, &opt, this);
489 int scrollBarSliderMin = style()->pixelMetric(QStyle::PM_ScrollBarSliderMin, &opt, this);
490 QSize size;
491 if (opt.orientation == Qt::Horizontal)
492 size = QSize(scrollBarExtent * 2 + scrollBarSliderMin, scrollBarExtent);
493 else
494 size = QSize(scrollBarExtent, scrollBarExtent * 2 + scrollBarSliderMin);
495
496 return style()->sizeFromContents(QStyle::CT_ScrollBar, &opt, size, this)
497 .expandedTo(QApplication::globalStrut());
498 }
499
500/*!\reimp */
501void QScrollBar::sliderChange(SliderChange change)
502{
503 QAbstractSlider::sliderChange(change);
504}
505
506/*!
507 \reimp
508*/
509bool QScrollBar::event(QEvent *event)
510{
511 switch(event->type()) {
512 case QEvent::HoverEnter:
513 case QEvent::HoverLeave:
514 case QEvent::HoverMove:
515 if (const QHoverEvent *he = static_cast<const QHoverEvent *>(event))
516 d_func()->updateHoverControl(he->pos());
517 break;
518 default:
519 break;
520 }
521 return QAbstractSlider::event(event);
522}
523
524/*!
525 \reimp
526*/
527void QScrollBar::paintEvent(QPaintEvent *)
528{
529 Q_D(QScrollBar);
530 QPainter p(this);
531 QStyleOptionSlider opt;
532 initStyleOption(&opt);
533 opt.subControls = QStyle::SC_All;
534 if (d->pressedControl) {
535 opt.activeSubControls = (QStyle::SubControl)d->pressedControl;
536 if (!d->pointerOutsidePressedControl)
537 opt.state |= QStyle::State_Sunken;
538 } else {
539 opt.activeSubControls = (QStyle::SubControl)d->hoverControl;
540 }
541 style()->drawComplexControl(QStyle::CC_ScrollBar, &opt, &p, this);
542}
543
544/*!
545 \reimp
546*/
547void QScrollBar::mousePressEvent(QMouseEvent *e)
548{
549 Q_D(QScrollBar);
550
551 if (d->repeatActionTimer.isActive())
552 d->stopRepeatAction();
553
554 bool midButtonAbsPos = style()->styleHint(QStyle::SH_ScrollBar_MiddleClickAbsolutePosition,
555 0, this);
556 QStyleOptionSlider opt;
557 initStyleOption(&opt);
558
559 if (d->maximum == d->minimum // no range
560 || (e->buttons() & (~e->button())) // another button was clicked before
561 || !(e->button() == Qt::LeftButton || (midButtonAbsPos && e->button() == Qt::MidButton)))
562 return;
563
564 d->pressedControl = style()->hitTestComplexControl(QStyle::CC_ScrollBar, &opt, e->pos(), this);
565 d->pointerOutsidePressedControl = false;
566
567 QRect sr = style()->subControlRect(QStyle::CC_ScrollBar, &opt,
568 QStyle::SC_ScrollBarSlider, this);
569 QPoint click = e->pos();
570 QPoint pressValue = click - sr.center() + sr.topLeft();
571 d->pressValue = d->orientation == Qt::Horizontal ? d->pixelPosToRangeValue(pressValue.x()) :
572 d->pixelPosToRangeValue(pressValue.y());
573 if (d->pressedControl == QStyle::SC_ScrollBarSlider) {
574 d->clickOffset = HORIZONTAL ? (click.x()-sr.x()) : (click.y()-sr.y());
575 d->snapBackPosition = d->position;
576 }
577
578 if ((d->pressedControl == QStyle::SC_ScrollBarAddPage
579 || d->pressedControl == QStyle::SC_ScrollBarSubPage)
580 && ((midButtonAbsPos && e->button() == Qt::MidButton)
581 || (style()->styleHint(QStyle::SH_ScrollBar_LeftClickAbsolutePosition, &opt, this)
582 && e->button() == Qt::LeftButton))) {
583 int sliderLength = HORIZONTAL ? sr.width() : sr.height();
584 setSliderPosition(d->pixelPosToRangeValue((HORIZONTAL ? e->pos().x()
585 : e->pos().y()) - sliderLength / 2));
586 d->pressedControl = QStyle::SC_ScrollBarSlider;
587 d->clickOffset = sliderLength / 2;
588 }
589 const int initialDelay = 500; // default threshold
590 d->activateControl(d->pressedControl, initialDelay);
591 QTime time;
592 time.start();
593 repaint(style()->subControlRect(QStyle::CC_ScrollBar, &opt, d->pressedControl, this));
594 if (time.elapsed() >= initialDelay && d->repeatActionTimer.isActive()) {
595 // It took more than 500ms (the initial timer delay) to process the repaint(), we
596 // therefore need to restart the timer in case we have a pending mouse release event;
597 // otherwise we'll get a timer event right before the release event,
598 // causing the repeat action to be invoked twice on a single mouse click.
599 // 50ms is the default repeat time (see activateControl/setRepeatAction).
600 d->repeatActionTimer.start(50, this);
601 }
602 if (d->pressedControl == QStyle::SC_ScrollBarSlider)
603 setSliderDown(true);
604}
605
606
607/*!
608 \reimp
609*/
610void QScrollBar::mouseReleaseEvent(QMouseEvent *e)
611{
612 Q_D(QScrollBar);
613 if (!d->pressedControl)
614 return;
615
616 if (e->buttons() & (~e->button())) // some other button is still pressed
617 return;
618
619 d->stopRepeatAction();
620}
621
622
623/*!
624 \reimp
625*/
626void QScrollBar::mouseMoveEvent(QMouseEvent *e)
627{
628 Q_D(QScrollBar);
629 if (!d->pressedControl)
630 return;
631
632 QStyleOptionSlider opt;
633 initStyleOption(&opt);
634 if (!(e->buttons() & Qt::LeftButton
635 || ((e->buttons() & Qt::MidButton)
636 && style()->styleHint(QStyle::SH_ScrollBar_MiddleClickAbsolutePosition, &opt, this))))
637 return;
638
639 if (d->pressedControl == QStyle::SC_ScrollBarSlider) {
640 QPoint click = e->pos();
641 int newPosition = d->pixelPosToRangeValue((HORIZONTAL ? click.x() : click.y()) -d->clickOffset);
642 int m = style()->pixelMetric(QStyle::PM_MaximumDragDistance, &opt, this);
643 if (m >= 0) {
644 QRect r = rect();
645 r.adjust(-m, -m, m, m);
646 if (! r.contains(e->pos()))
647 newPosition = d->snapBackPosition;
648 }
649 setSliderPosition(newPosition);
650 } else if (!style()->styleHint(QStyle::SH_ScrollBar_ScrollWhenPointerLeavesControl, &opt, this)) {
651
652 if (style()->styleHint(QStyle::SH_ScrollBar_RollBetweenButtons, &opt, this)
653 && d->pressedControl & (QStyle::SC_ScrollBarAddLine | QStyle::SC_ScrollBarSubLine)) {
654 QStyle::SubControl newSc = style()->hitTestComplexControl(QStyle::CC_ScrollBar, &opt, e->pos(), this);
655 if (newSc == d->pressedControl && !d->pointerOutsidePressedControl)
656 return; // nothing to do
657 if (newSc & (QStyle::SC_ScrollBarAddLine | QStyle::SC_ScrollBarSubLine)) {
658 d->pointerOutsidePressedControl = false;
659 QRect scRect = style()->subControlRect(QStyle::CC_ScrollBar, &opt, newSc, this);
660 scRect |= style()->subControlRect(QStyle::CC_ScrollBar, &opt, d->pressedControl, this);
661 d->pressedControl = newSc;
662 d->activateControl(d->pressedControl, 0);
663 update(scRect);
664 return;
665 }
666 }
667
668 // stop scrolling when the mouse pointer leaves a control
669 // similar to push buttons
670 QRect pr = style()->subControlRect(QStyle::CC_ScrollBar, &opt, d->pressedControl, this);
671 if (pr.contains(e->pos()) == d->pointerOutsidePressedControl) {
672 if ((d->pointerOutsidePressedControl = !d->pointerOutsidePressedControl)) {
673 d->pointerOutsidePressedControl = true;
674 setRepeatAction(SliderNoAction);
675 repaint(pr);
676 } else {
677 d->activateControl(d->pressedControl);
678 }
679 }
680 }
681}
682
683
684int QScrollBarPrivate::pixelPosToRangeValue(int pos) const
685{
686 Q_Q(const QScrollBar);
687 QStyleOptionSlider opt;
688 q->initStyleOption(&opt);
689 QRect gr = q->style()->subControlRect(QStyle::CC_ScrollBar, &opt,
690 QStyle::SC_ScrollBarGroove, q);
691 QRect sr = q->style()->subControlRect(QStyle::CC_ScrollBar, &opt,
692 QStyle::SC_ScrollBarSlider, q);
693 int sliderMin, sliderMax, sliderLength;
694
695 if (orientation == Qt::Horizontal) {
696 sliderLength = sr.width();
697 sliderMin = gr.x();
698 sliderMax = gr.right() - sliderLength + 1;
699 if (q->layoutDirection() == Qt::RightToLeft)
700 opt.upsideDown = !opt.upsideDown;
701 } else {
702 sliderLength = sr.height();
703 sliderMin = gr.y();
704 sliderMax = gr.bottom() - sliderLength + 1;
705 }
706
707 return QStyle::sliderValueFromPosition(minimum, maximum, pos - sliderMin,
708 sliderMax - sliderMin, opt.upsideDown);
709}
710
711/*! \reimp
712*/
713void QScrollBar::hideEvent(QHideEvent *)
714{
715 Q_D(QScrollBar);
716 if (d->pressedControl) {
717 d->pressedControl = QStyle::SC_None;
718 setRepeatAction(SliderNoAction);
719 }
720}
721
722/*!
723 \fn bool QScrollBar::draggingSlider()
724
725 Use isSliderDown() instead.
726*/
727
728/*! \internal
729 Returns the style option for scroll bar.
730*/
731Q_GUI_EXPORT QStyleOptionSlider qt_qscrollbarStyleOption(QScrollBar *scrollbar)
732{
733 QStyleOptionSlider opt;
734 scrollbar->initStyleOption(&opt);
735 return opt;
736}
737
738QT_END_NAMESPACE
739
740#endif // QT_NO_SCROLLBAR
Note: See TracBrowser for help on using the repository browser.