1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtGui module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
44 #ifndef QT_NO_ACCESSIBILITY
45 #include "qaccessible.h"
47 #include "qapplication.h"
51 #include "qstyleoption.h"
52 #include "private/qabstractslider_p.h"
57 class QSliderPrivate : public QAbstractSliderPrivate
59 Q_DECLARE_PUBLIC(QSlider)
61 QStyle::SubControl pressedControl;
63 QSlider::TickPosition tickPosition;
66 void resetLayoutItemMargins();
67 int pixelPosToRangeValue(int pos) const;
68 inline int pick(const QPoint &pt) const;
70 QStyle::SubControl newHoverControl(const QPoint &pos);
71 bool updateHoverControl(const QPoint &pos);
72 QStyle::SubControl hoverControl;
76 void QSliderPrivate::init()
79 pressedControl = QStyle::SC_None;
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)
88 q->setAttribute(Qt::WA_WState_OwnSizePolicy, false);
89 resetLayoutItemMargins();
92 void QSliderPrivate::resetLayoutItemMargins()
95 QStyleOptionSlider opt;
96 q->initStyleOption(&opt);
97 setLayoutItemMargins(QStyle::SE_SliderLayoutItem, &opt);
100 int QSliderPrivate::pixelPosToRangeValue(int pos) const
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;
109 if (orientation == Qt::Horizontal) {
110 sliderLength = sr.width();
112 sliderMax = gr.right() - sliderLength + 1;
114 sliderLength = sr.height();
116 sliderMax = gr.bottom() - sliderLength + 1;
118 return QStyle::sliderValueFromPosition(minimum, maximum, pos - sliderMin,
119 sliderMax - sliderMin, opt.upsideDown);
122 inline int QSliderPrivate::pick(const QPoint &pt) const
124 return orientation == Qt::Horizontal ? pt.x() : pt.y();
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.
132 \sa QStyleOption::initFrom()
134 void QSlider::initStyleOption(QStyleOptionSlider *option) const
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;
160 bool QSliderPrivate::updateHoverControl(const QPoint &pos)
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);
174 QStyle::SubControl QSliderPrivate::newHoverControl(const QPoint &pos)
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);
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;
195 hoverControl = QStyle::SC_None;
203 \brief The QSlider widget provides a vertical or horizontal slider.
205 \ingroup basicwidgets
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.
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.
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.
226 QSlider inherits a comprehensive set of signals:
228 \header \li Signal \li Description
229 \row \li \l valueChanged()
230 \li Emitted when the slider's value has changed. The tracking()
231 determines whether this signal is emitted during user
233 \row \li \l sliderPressed()
234 \li Emitted when the user starts to drag the slider.
235 \row \li \l sliderMoved()
236 \li Emitted when the user drags the slider.
237 \row \li \l sliderReleased()
238 \li Emitted when the user releases the slider.
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.
245 A slider accepts focus on Tab and provides both a mouse wheel and a
246 keyboard interface. The keyboard interface is the following:
249 \li Left/Right move a horizontal slider by one single step.
250 \li Up/Down move a vertical slider by one single step.
251 \li PageUp moves up one page.
252 \li PageDown moves down one page.
253 \li Home moves to the start (mininum).
254 \li End moves to the end (maximum).
258 \row \li \inlineimage macintosh-slider.png Screenshot of a Macintosh slider
259 \li A slider shown in the \l{Macintosh Style Widget Gallery}{Macintosh widget style}.
260 \row \li \inlineimage windows-slider.png Screenshot of a Windows XP slider
261 \li A slider shown in the \l{Windows XP Style Widget Gallery}{Windows XP widget style}.
262 \row \li \inlineimage plastique-slider.png Screenshot of a Plastique slider
263 \li A slider shown in the \l{Plastique Style Widget Gallery}{Plastique widget style}.
266 \sa QScrollBar, QSpinBox, QDial, {fowler}{GUI Design Handbook: Slider}, {Sliders Example}
271 \enum QSlider::TickPosition
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.
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
293 Constructs a vertical slider with the given \a parent.
295 QSlider::QSlider(QWidget *parent)
296 : QAbstractSlider(*new QSliderPrivate, parent)
298 d_func()->orientation = Qt::Vertical;
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.
308 QSlider::QSlider(Qt::Orientation orientation, QWidget *parent)
309 : QAbstractSlider(*new QSliderPrivate, parent)
311 d_func()->orientation = orientation;
317 Destroys this slider.
326 void QSlider::paintEvent(QPaintEvent *)
330 QStyleOptionSlider opt;
331 initStyleOption(&opt);
333 opt.subControls = QStyle::SC_SliderGroove | QStyle::SC_SliderHandle;
334 if (d->tickPosition != NoTicks)
335 opt.subControls |= QStyle::SC_SliderTickmarks;
336 if (d->pressedControl) {
337 opt.activeSubControls = d->pressedControl;
338 opt.state |= QStyle::State_Sunken;
340 opt.activeSubControls = d->hoverControl;
343 style()->drawComplexControl(QStyle::CC_Slider, &opt, &p, this);
350 bool QSlider::event(QEvent *event)
354 switch(event->type()) {
355 case QEvent::HoverEnter:
356 case QEvent::HoverLeave:
357 case QEvent::HoverMove:
358 if (const QHoverEvent *he = static_cast<const QHoverEvent *>(event))
359 d->updateHoverControl(he->pos());
361 case QEvent::StyleChange:
362 case QEvent::MacSizeChange:
363 d->resetLayoutItemMargins();
368 return QAbstractSlider::event(event);
374 void QSlider::mousePressEvent(QMouseEvent *ev)
377 if (d->maximum == d->minimum || (ev->buttons() ^ ev->button())) {
381 #ifdef QT_KEYPAD_NAVIGATION
382 if (QApplication::keypadNavigationEnabled())
386 if ((ev->button() & style()->styleHint(QStyle::SH_Slider_AbsoluteSetButtons)) == ev->button()) {
387 QStyleOptionSlider opt;
388 initStyleOption(&opt);
389 const QRect sliderRect = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
390 const QPoint center = sliderRect.center() - sliderRect.topLeft();
391 // to take half of the slider off for the setSliderPosition call we use the center - topLeft
393 setSliderPosition(d->pixelPosToRangeValue(d->pick(ev->pos() - center)));
394 triggerAction(SliderMove);
395 setRepeatAction(SliderNoAction);
396 d->pressedControl = QStyle::SC_SliderHandle;
398 } else if ((ev->button() & style()->styleHint(QStyle::SH_Slider_PageSetButtons)) == ev->button()) {
399 QStyleOptionSlider opt;
400 initStyleOption(&opt);
401 d->pressedControl = style()->hitTestComplexControl(QStyle::CC_Slider,
402 &opt, ev->pos(), this);
403 SliderAction action = SliderNoAction;
404 if (d->pressedControl == QStyle::SC_SliderGroove) {
405 const QRect sliderRect = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
406 int pressValue = d->pixelPosToRangeValue(d->pick(ev->pos() - sliderRect.center() + sliderRect.topLeft()));
407 d->pressValue = pressValue;
408 if (pressValue > d->value)
409 action = SliderPageStepAdd;
410 else if (pressValue < d->value)
411 action = SliderPageStepSub;
413 triggerAction(action);
414 setRepeatAction(action);
422 if (d->pressedControl == QStyle::SC_SliderHandle) {
423 QStyleOptionSlider opt;
424 initStyleOption(&opt);
425 setRepeatAction(SliderNoAction);
426 QRect sr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
427 d->clickOffset = d->pick(ev->pos() - sr.topLeft());
436 void QSlider::mouseMoveEvent(QMouseEvent *ev)
439 if (d->pressedControl != QStyle::SC_SliderHandle) {
444 int newPosition = d->pixelPosToRangeValue(d->pick(ev->pos()) - d->clickOffset);
445 QStyleOptionSlider opt;
446 initStyleOption(&opt);
447 setSliderPosition(newPosition);
454 void QSlider::mouseReleaseEvent(QMouseEvent *ev)
457 if (d->pressedControl == QStyle::SC_None || ev->buttons()) {
462 QStyle::SubControl oldPressed = QStyle::SubControl(d->pressedControl);
463 d->pressedControl = QStyle::SC_None;
464 setRepeatAction(SliderNoAction);
465 if (oldPressed == QStyle::SC_SliderHandle)
466 setSliderDown(false);
467 QStyleOptionSlider opt;
468 initStyleOption(&opt);
469 opt.subControls = oldPressed;
470 update(style()->subControlRect(QStyle::CC_Slider, &opt, oldPressed, this));
476 QSize QSlider::sizeHint() const
480 const int SliderLength = 84, TickSpace = 5;
481 QStyleOptionSlider opt;
482 initStyleOption(&opt);
483 int thick = style()->pixelMetric(QStyle::PM_SliderThickness, &opt, this);
484 if (d->tickPosition & TicksAbove)
486 if (d->tickPosition & TicksBelow)
488 int w = thick, h = SliderLength;
489 if (d->orientation == Qt::Horizontal) {
493 return style()->sizeFromContents(QStyle::CT_Slider, &opt, QSize(w, h), this).expandedTo(QApplication::globalStrut());
499 QSize QSlider::minimumSizeHint() const
502 QSize s = sizeHint();
503 QStyleOptionSlider opt;
504 initStyleOption(&opt);
505 int length = style()->pixelMetric(QStyle::PM_SliderLength, &opt, this);
506 if (d->orientation == Qt::Horizontal)
514 \property QSlider::tickPosition
515 \brief the tickmark position for this slider
517 The valid values are described by the QSlider::TickPosition enum.
519 The default value is \l QSlider::NoTicks.
524 void QSlider::setTickPosition(TickPosition position)
527 d->tickPosition = position;
528 d->resetLayoutItemMargins();
533 QSlider::TickPosition QSlider::tickPosition() const
535 return d_func()->tickPosition;
539 \property QSlider::tickInterval
540 \brief the interval between tickmarks
542 This is a value interval, not a pixel interval. If it is 0, the
543 slider will choose between singleStep() and pageStep().
545 The default value is 0.
547 \sa tickPosition, lineStep(), pageStep()
550 void QSlider::setTickInterval(int ts)
552 d_func()->tickInterval = qMax(0, ts);
556 int QSlider::tickInterval() const
558 return d_func()->tickInterval;
562 \fn void QSlider::addStep()
564 Use setValue() instead.
568 \fn void QSlider::subtractStep()
570 Use setValue() instead.
574 Returns the style option for slider.
576 Q_WIDGETS_EXPORT QStyleOptionSlider qt_qsliderStyleOption(QSlider *slider)
578 QStyleOptionSlider sliderOption;
579 slider->initStyleOption(&sliderOption);