source: trunk/src/gui/widgets/qslider.cpp@ 603

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

trunk: Merged in qt 4.6.1 sources.

File size: 20.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
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 [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qslider.h"
43#ifndef QT_NO_SLIDER
44#ifndef QT_NO_ACCESSIBILITY
45#include "qaccessible.h"
46#endif
47#include "qapplication.h"
48#include "qevent.h"
49#include "qpainter.h"
50#include "qstyle.h"
51#include "qstyleoption.h"
52#include "private/qabstractslider_p.h"
53#include "qdebug.h"
54
55QT_BEGIN_NAMESPACE
56
57class QSliderPrivate : public QAbstractSliderPrivate
58{
59 Q_DECLARE_PUBLIC(QSlider)
60public:
61 QStyle::SubControl pressedControl;
62 int tickInterval;
63 QSlider::TickPosition tickPosition;
64 int clickOffset;
65 void init();
66 void resetLayoutItemMargins();
67 int pixelPosToRangeValue(int pos) const;
68 inline int pick(const QPoint &pt) const;
69
70 QStyle::SubControl newHoverControl(const QPoint &pos);
71 bool updateHoverControl(const QPoint &pos);
72 QStyle::SubControl hoverControl;
73 QRect hoverRect;
74};
75
76void QSliderPrivate::init()
77{
78 Q_Q(QSlider);
79 pressedControl = QStyle::SC_None;
80 tickInterval = 0;
81 tickPosition = QSlider::NoTicks;
82 hoverControl = QStyle::SC_None;
83 q->setFocusPolicy(Qt::FocusPolicy(q->style()->styleHint(QStyle::SH_Button_FocusPolicy)));
84 QSizePolicy sp(QSizePolicy::Expanding, QSizePolicy::Fixed, QSizePolicy::Slider);
85 if (orientation == Qt::Vertical)
86 sp.transpose();
87 q->setSizePolicy(sp);
88 q->setAttribute(Qt::WA_WState_OwnSizePolicy, false);
89 resetLayoutItemMargins();
90}
91
92void QSliderPrivate::resetLayoutItemMargins()
93{
94 Q_Q(QSlider);
95 QStyleOptionSlider opt;
96 q->initStyleOption(&opt);
97 setLayoutItemMargins(QStyle::SE_SliderLayoutItem, &opt);
98}
99
100int QSliderPrivate::pixelPosToRangeValue(int pos) const
101{
102 Q_Q(const QSlider);
103 QStyleOptionSlider opt;
104 q->initStyleOption(&opt);
105 QRect gr = q->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, q);
106 QRect sr = q->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, q);
107 int sliderMin, sliderMax, sliderLength;
108
109 if (orientation == Qt::Horizontal) {
110 sliderLength = sr.width();
111 sliderMin = gr.x();
112 sliderMax = gr.right() - sliderLength + 1;
113 } else {
114 sliderLength = sr.height();
115 sliderMin = gr.y();
116 sliderMax = gr.bottom() - sliderLength + 1;
117 }
118 return QStyle::sliderValueFromPosition(minimum, maximum, pos - sliderMin,
119 sliderMax - sliderMin, opt.upsideDown);
120}
121
122inline int QSliderPrivate::pick(const QPoint &pt) const
123{
124 return orientation == Qt::Horizontal ? pt.x() : pt.y();
125}
126
127/*!
128 Initialize \a option with the values from this QSlider. This method
129 is useful for subclasses when they need a QStyleOptionSlider, but don't want
130 to fill in all the information themselves.
131
132 \sa QStyleOption::initFrom()
133*/
134void QSlider::initStyleOption(QStyleOptionSlider *option) const
135{
136 if (!option)
137 return;
138
139 Q_D(const QSlider);
140 option->initFrom(this);
141 option->subControls = QStyle::SC_None;
142 option->activeSubControls = QStyle::SC_None;
143 option->orientation = d->orientation;
144 option->maximum = d->maximum;
145 option->minimum = d->minimum;
146 option->tickPosition = (QSlider::TickPosition)d->tickPosition;
147 option->tickInterval = d->tickInterval;
148 option->upsideDown = (d->orientation == Qt::Horizontal) ?
149 (d->invertedAppearance != (option->direction == Qt::RightToLeft))
150 : (!d->invertedAppearance);
151 option->direction = Qt::LeftToRight; // we use the upsideDown option instead
152 option->sliderPosition = d->position;
153 option->sliderValue = d->value;
154 option->singleStep = d->singleStep;
155 option->pageStep = d->pageStep;
156 if (d->orientation == Qt::Horizontal)
157 option->state |= QStyle::State_Horizontal;
158}
159
160bool QSliderPrivate::updateHoverControl(const QPoint &pos)
161{
162 Q_Q(QSlider);
163 QRect lastHoverRect = hoverRect;
164 QStyle::SubControl lastHoverControl = hoverControl;
165 bool doesHover = q->testAttribute(Qt::WA_Hover);
166 if (lastHoverControl != newHoverControl(pos) && doesHover) {
167 q->update(lastHoverRect);
168 q->update(hoverRect);
169 return true;
170 }
171 return !doesHover;
172}
173
174QStyle::SubControl QSliderPrivate::newHoverControl(const QPoint &pos)
175{
176 Q_Q(QSlider);
177 QStyleOptionSlider opt;
178 q->initStyleOption(&opt);
179 opt.subControls = QStyle::SC_All;
180 QRect handleRect = q->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, q);
181 QRect grooveRect = q->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, q);
182 QRect tickmarksRect = q->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderTickmarks, q);
183
184 if (handleRect.contains(pos)) {
185 hoverRect = handleRect;
186 hoverControl = QStyle::SC_SliderHandle;
187 } else if (grooveRect.contains(pos)) {
188 hoverRect = grooveRect;
189 hoverControl = QStyle::SC_SliderGroove;
190 } else if (tickmarksRect.contains(pos)) {
191 hoverRect = tickmarksRect;
192 hoverControl = QStyle::SC_SliderTickmarks;
193 } else {
194 hoverRect = QRect();
195 hoverControl = QStyle::SC_None;
196 }
197
198 return hoverControl;
199}
200
201/*!
202 \class QSlider
203 \brief The QSlider widget provides a vertical or horizontal slider.
204
205 \ingroup basicwidgets
206
207
208 The slider is the classic widget for controlling a bounded value.
209 It lets the user move a slider handle along a horizontal or vertical
210 groove and translates the handle's position into an integer value
211 within the legal range.
212
213 QSlider has very few of its own functions; most of the functionality is in
214 QAbstractSlider. The most useful functions are setValue() to set
215 the slider directly to some value; triggerAction() to simulate
216 the effects of clicking (useful for shortcut keys);
217 setSingleStep(), setPageStep() to set the steps; and setMinimum()
218 and setMaximum() to define the range of the scroll bar.
219
220 QSlider provides methods for controlling tickmarks. You can use
221 setTickPosition() to indicate where you want the tickmarks to be,
222 setTickInterval() to indicate how many of them you want. the
223 currently set tick position and interval can be queried using the
224 tickPosition() and tickInterval() functions, respectively.
225
226 QSlider inherits a comprehensive set of signals:
227 \table
228 \header \o Signal \o Description
229 \row \o \l valueChanged()
230 \o Emitted when the slider's value has changed. The tracking()
231 determines whether this signal is emitted during user
232 interaction.
233 \row \o \l sliderPressed()
234 \o Emitted when the user starts to drag the slider.
235 \row \o \l sliderMoved()
236 \o Emitted when the user drags the slider.
237 \row \o \l sliderReleased()
238 \o Emitted when the user releases the slider.
239 \endtable
240
241 QSlider only provides integer ranges. Note that although
242 QSlider handles very large numbers, it becomes difficult for users
243 to use a slider accurately for very large ranges.
244
245 A slider accepts focus on Tab and provides both a mouse wheel and a
246 keyboard interface. The keyboard interface is the following:
247
248 \list
249 \o Left/Right move a horizontal slider by one single step.
250 \o Up/Down move a vertical slider by one single step.
251 \o PageUp moves up one page.
252 \o PageDown moves down one page.
253 \o Home moves to the start (mininum).
254 \o End moves to the end (maximum).
255 \endlist
256
257 \table 100%
258 \row \o \inlineimage macintosh-slider.png Screenshot of a Macintosh slider
259 \o A slider shown in the \l{Macintosh Style Widget Gallery}{Macintosh widget style}.
260 \row \o \inlineimage windows-slider.png Screenshot of a Windows XP slider
261 \o A slider shown in the \l{Windows XP Style Widget Gallery}{Windows XP widget style}.
262 \row \o \inlineimage plastique-slider.png Screenshot of a Plastique slider
263 \o A slider shown in the \l{Plastique Style Widget Gallery}{Plastique widget style}.
264 \endtable
265
266 \sa QScrollBar, QSpinBox, QDial, {fowler}{GUI Design Handbook: Slider}, {Sliders Example}
267*/
268
269
270/*!
271 \enum QSlider::TickPosition
272
273 This enum specifies where the tick marks are to be drawn relative
274 to the slider's groove and the handle the user moves.
275
276 \value NoTicks Do not draw any tick marks.
277 \value TicksBothSides Draw tick marks on both sides of the groove.
278 \value TicksAbove Draw tick marks above the (horizontal) slider
279 \value TicksBelow Draw tick marks below the (horizontal) slider
280 \value TicksLeft Draw tick marks to the left of the (vertical) slider
281 \value TicksRight Draw tick marks to the right of the (vertical) slider
282
283 \omitvalue NoMarks
284 \omitvalue Above
285 \omitvalue Left
286 \omitvalue Below
287 \omitvalue Right
288 \omitvalue Both
289*/
290
291
292/*!
293 Constructs a vertical slider with the given \a parent.
294*/
295QSlider::QSlider(QWidget *parent)
296 : QAbstractSlider(*new QSliderPrivate, parent)
297{
298 d_func()->orientation = Qt::Vertical;
299 d_func()->init();
300}
301
302/*!
303 Constructs a slider with the given \a parent. The \a orientation
304 parameter determines whether the slider is horizontal or vertical;
305 the valid values are Qt::Vertical and Qt::Horizontal.
306*/
307
308QSlider::QSlider(Qt::Orientation orientation, QWidget *parent)
309 : QAbstractSlider(*new QSliderPrivate, parent)
310{
311 d_func()->orientation = orientation;
312 d_func()->init();
313}
314
315#ifdef QT3_SUPPORT
316/*!
317 Use QSlider() and QObject::setObjectName() instead.
318
319 \oldcode
320 QSlider *mySlider = new QSlider(parent, name);
321 \newcode
322 QSlider *mySlider = new QSlider(parent);
323 mySlider->setObjectName(name);
324 \endcode
325*/
326QSlider::QSlider(QWidget *parent, const char *name)
327 : QAbstractSlider(*new QSliderPrivate, parent)
328{
329 setObjectName(QString::fromAscii(name));
330 d_func()->orientation = Qt::Vertical;
331 d_func()->init();
332}
333
334/*!
335 Use QSlider() and QObject::setObjectName() instead.
336
337 \oldcode
338 QSlider *mySlider = new QSlider(orientation, parent, name);
339 \newcode
340 QSlider *mySlider = new QSlider(orientation, parent);
341 mySlider->setObjectName(name);
342 \endcode
343*/
344QSlider::QSlider(Qt::Orientation orientation, QWidget *parent, const char *name)
345 : QAbstractSlider(*new QSliderPrivate, parent)
346{
347 setObjectName(QString::fromAscii(name));
348 d_func()->orientation = orientation;
349 d_func()->init();
350}
351
352/*!
353 Use QSlider(), QObject::setObjectName() and the functionality
354 inherited from QAbstractSlider instead.
355
356 \oldcode
357 QSlider *mySlider = new QSlider(minValue, maxValue, pageStep,
358 value, orientation, parent, name);
359 \newcode
360 QSlider *mySlider = new QSlider(orientation, parent);
361 mySlider->setObjectName(name);
362 mySlider->setMinimum(minValue);
363 mySlider->setMaximum(maxValue);
364 mySlider->setPageStep(pageStep);
365 mySlider->setValue(value);
366 \endcode
367*/
368QSlider::QSlider(int minValue, int maxValue, int pageStep, int value, Qt::Orientation orientation,
369 QWidget *parent, const char *name)
370 : QAbstractSlider(*new QSliderPrivate, parent)
371{
372 Q_D(QSlider);
373 setObjectName(QString::fromAscii(name));
374 d->minimum = minValue;
375 d->maximum = maxValue;
376 d->pageStep = pageStep;
377 d->position = d->value = value;
378 d->orientation = orientation;
379 d->init();
380}
381#endif
382
383/*!
384 Destroys this slider.
385*/
386QSlider::~QSlider()
387{
388}
389
390/*!
391 \reimp
392*/
393void QSlider::paintEvent(QPaintEvent *)
394{
395 Q_D(QSlider);
396 QPainter p(this);
397 QStyleOptionSlider opt;
398 initStyleOption(&opt);
399
400 opt.subControls = QStyle::SC_SliderGroove | QStyle::SC_SliderHandle;
401 if (d->tickPosition != NoTicks)
402 opt.subControls |= QStyle::SC_SliderTickmarks;
403 if (d->pressedControl) {
404 opt.activeSubControls = d->pressedControl;
405 opt.state |= QStyle::State_Sunken;
406 } else {
407 opt.activeSubControls = d->hoverControl;
408 }
409
410 style()->drawComplexControl(QStyle::CC_Slider, &opt, &p, this);
411}
412
413/*!
414 \reimp
415*/
416
417bool QSlider::event(QEvent *event)
418{
419 Q_D(QSlider);
420
421 switch(event->type()) {
422 case QEvent::HoverEnter:
423 case QEvent::HoverLeave:
424 case QEvent::HoverMove:
425 if (const QHoverEvent *he = static_cast<const QHoverEvent *>(event))
426 d->updateHoverControl(he->pos());
427 break;
428 case QEvent::StyleChange:
429 case QEvent::MacSizeChange:
430 d->resetLayoutItemMargins();
431 break;
432 default:
433 break;
434 }
435 return QAbstractSlider::event(event);
436}
437
438/*!
439 \reimp
440*/
441void QSlider::mousePressEvent(QMouseEvent *ev)
442{
443 Q_D(QSlider);
444 if (d->maximum == d->minimum || (ev->buttons() ^ ev->button())) {
445 ev->ignore();
446 return;
447 }
448#ifdef QT_KEYPAD_NAVIGATION
449 if (QApplication::keypadNavigationEnabled())
450 setEditFocus(true);
451#endif
452 ev->accept();
453 if ((ev->button() & style()->styleHint(QStyle::SH_Slider_AbsoluteSetButtons)) == ev->button()) {
454 QStyleOptionSlider opt;
455 initStyleOption(&opt);
456 const QRect sliderRect = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
457 const QPoint center = sliderRect.center() - sliderRect.topLeft();
458 // to take half of the slider off for the setSliderPosition call we use the center - topLeft
459
460 setSliderPosition(d->pixelPosToRangeValue(d->pick(ev->pos() - center)));
461 triggerAction(SliderMove);
462 setRepeatAction(SliderNoAction);
463 d->pressedControl = QStyle::SC_SliderHandle;
464 update();
465 } else if ((ev->button() & style()->styleHint(QStyle::SH_Slider_PageSetButtons)) == ev->button()) {
466 QStyleOptionSlider opt;
467 initStyleOption(&opt);
468 d->pressedControl = style()->hitTestComplexControl(QStyle::CC_Slider,
469 &opt, ev->pos(), this);
470 SliderAction action = SliderNoAction;
471 if (d->pressedControl == QStyle::SC_SliderGroove) {
472 const QRect sliderRect = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
473 int pressValue = d->pixelPosToRangeValue(d->pick(ev->pos() - sliderRect.center() + sliderRect.topLeft()));
474 d->pressValue = pressValue;
475 if (pressValue > d->value)
476 action = SliderPageStepAdd;
477 else if (pressValue < d->value)
478 action = SliderPageStepSub;
479 if (action) {
480 triggerAction(action);
481 setRepeatAction(action);
482 }
483 }
484 } else {
485 ev->ignore();
486 return;
487 }
488
489 if (d->pressedControl == QStyle::SC_SliderHandle) {
490 QStyleOptionSlider opt;
491 initStyleOption(&opt);
492 setRepeatAction(SliderNoAction);
493 QRect sr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
494 d->clickOffset = d->pick(ev->pos() - sr.topLeft());
495 update(sr);
496 setSliderDown(true);
497 }
498}
499
500/*!
501 \reimp
502*/
503void QSlider::mouseMoveEvent(QMouseEvent *ev)
504{
505 Q_D(QSlider);
506 if (d->pressedControl != QStyle::SC_SliderHandle) {
507 ev->ignore();
508 return;
509 }
510 ev->accept();
511 int newPosition = d->pixelPosToRangeValue(d->pick(ev->pos()) - d->clickOffset);
512 QStyleOptionSlider opt;
513 initStyleOption(&opt);
514 setSliderPosition(newPosition);
515}
516
517
518/*!
519 \reimp
520*/
521void QSlider::mouseReleaseEvent(QMouseEvent *ev)
522{
523 Q_D(QSlider);
524 if (d->pressedControl == QStyle::SC_None || ev->buttons()) {
525 ev->ignore();
526 return;
527 }
528 ev->accept();
529 QStyle::SubControl oldPressed = QStyle::SubControl(d->pressedControl);
530 d->pressedControl = QStyle::SC_None;
531 setRepeatAction(SliderNoAction);
532 if (oldPressed == QStyle::SC_SliderHandle)
533 setSliderDown(false);
534 QStyleOptionSlider opt;
535 initStyleOption(&opt);
536 opt.subControls = oldPressed;
537 update(style()->subControlRect(QStyle::CC_Slider, &opt, oldPressed, this));
538}
539
540/*!
541 \reimp
542*/
543QSize QSlider::sizeHint() const
544{
545 Q_D(const QSlider);
546 ensurePolished();
547 const int SliderLength = 84, TickSpace = 5;
548 QStyleOptionSlider opt;
549 initStyleOption(&opt);
550 int thick = style()->pixelMetric(QStyle::PM_SliderThickness, &opt, this);
551 if (d->tickPosition & TicksAbove)
552 thick += TickSpace;
553 if (d->tickPosition & TicksBelow)
554 thick += TickSpace;
555 int w = thick, h = SliderLength;
556 if (d->orientation == Qt::Horizontal) {
557 w = SliderLength;
558 h = thick;
559 }
560 return style()->sizeFromContents(QStyle::CT_Slider, &opt, QSize(w, h), this).expandedTo(QApplication::globalStrut());
561}
562
563/*!
564 \reimp
565*/
566QSize QSlider::minimumSizeHint() const
567{
568 Q_D(const QSlider);
569 QSize s = sizeHint();
570 QStyleOptionSlider opt;
571 initStyleOption(&opt);
572 int length = style()->pixelMetric(QStyle::PM_SliderLength, &opt, this);
573 if (d->orientation == Qt::Horizontal)
574 s.setWidth(length);
575 else
576 s.setHeight(length);
577 return s;
578}
579
580/*!
581 \property QSlider::tickPosition
582 \brief the tickmark position for this slider
583
584 The valid values are described by the QSlider::TickPosition enum.
585
586 The default value is \l QSlider::NoTicks.
587
588 \sa tickInterval
589*/
590
591void QSlider::setTickPosition(TickPosition position)
592{
593 Q_D(QSlider);
594 d->tickPosition = position;
595 d->resetLayoutItemMargins();
596 update();
597 updateGeometry();
598}
599
600QSlider::TickPosition QSlider::tickPosition() const
601{
602 return d_func()->tickPosition;
603}
604
605/*!
606 \fn TickPosition QSlider::tickmarks() const
607 \compat
608
609 Use tickPosition() instead.
610*/
611
612/*!
613 \fn QSlider::setTickmarks(TickPosition position)
614 \compat
615
616 Use setTickPosition() instead.
617*/
618
619/*!
620 \property QSlider::tickInterval
621 \brief the interval between tickmarks
622
623 This is a value interval, not a pixel interval. If it is 0, the
624 slider will choose between lineStep() and pageStep().
625
626 The default value is 0.
627
628 \sa tickPosition, lineStep(), pageStep()
629*/
630
631void QSlider::setTickInterval(int ts)
632{
633 d_func()->tickInterval = qMax(0, ts);
634 update();
635}
636
637int QSlider::tickInterval() const
638{
639 return d_func()->tickInterval;
640}
641
642/*!
643 \fn void QSlider::addStep()
644
645 Use setValue() instead.
646*/
647
648/*!
649 \fn void QSlider::subtractStep()
650
651 Use setValue() instead.
652*/
653
654/*! \internal
655 Returns the style option for slider.
656*/
657Q_GUI_EXPORT QStyleOptionSlider qt_qsliderStyleOption(QSlider *slider)
658{
659 QStyleOptionSlider sliderOption;
660 slider->initStyleOption(&sliderOption);
661 return sliderOption;
662}
663
664#endif
665
666QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.