|
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 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
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 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
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 |
|
60 QT_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 |
|
204 class QScrollBarPrivate : public QAbstractSliderPrivate |
|
205 { |
|
206 Q_DECLARE_PUBLIC(QScrollBar) |
|
207 public: |
|
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 |
|
224 bool 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 |
|
238 QStyle::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 |
|
252 void 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 |
|
284 void 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 */ |
|
306 void 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 */ |
|
342 QScrollBar::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 */ |
|
360 QScrollBar::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 */ |
|
373 QScrollBar::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 */ |
|
385 QScrollBar::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 */ |
|
397 QScrollBar::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 */ |
|
417 QScrollBar::~QScrollBar() |
|
418 { |
|
419 } |
|
420 |
|
421 void 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 */ |
|
444 void 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 */ |
|
488 QSize 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 */ |
|
507 void QScrollBar::sliderChange(SliderChange change) |
|
508 { |
|
509 QAbstractSlider::sliderChange(change); |
|
510 } |
|
511 |
|
512 /*! |
|
513 \reimp |
|
514 */ |
|
515 bool 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 */ |
|
533 void 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 */ |
|
553 void 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 */ |
|
616 void 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 */ |
|
632 void 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 |
|
690 int 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 */ |
|
719 void 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 */ |
|
737 Q_GUI_EXPORT QStyleOptionSlider qt_qscrollbarStyleOption(QScrollBar *scrollbar) |
|
738 { |
|
739 QStyleOptionSlider opt; |
|
740 scrollbar->initStyleOption(&opt); |
|
741 return opt; |
|
742 } |
|
743 |
|
744 QT_END_NAMESPACE |
|
745 |
|
746 #endif // QT_NO_SCROLLBAR |