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
286 Constructs a vertical slider with the given \a parent.
288 QSlider::QSlider(QWidget *parent)
289 : QAbstractSlider(*new QSliderPrivate, parent)
291 d_func()->orientation = Qt::Vertical;
296 Constructs a slider with the given \a parent. The \a orientation
297 parameter determines whether the slider is horizontal or vertical;
298 the valid values are Qt::Vertical and Qt::Horizontal.
301 QSlider::QSlider(Qt::Orientation orientation, QWidget *parent)
302 : QAbstractSlider(*new QSliderPrivate, parent)
304 d_func()->orientation = orientation;
310 Destroys this slider.
319 void QSlider::paintEvent(QPaintEvent *)
323 QStyleOptionSlider opt;
324 initStyleOption(&opt);
326 opt.subControls = QStyle::SC_SliderGroove | QStyle::SC_SliderHandle;
327 if (d->tickPosition != NoTicks)
328 opt.subControls |= QStyle::SC_SliderTickmarks;
329 if (d->pressedControl) {
330 opt.activeSubControls = d->pressedControl;
331 opt.state |= QStyle::State_Sunken;
333 opt.activeSubControls = d->hoverControl;
336 style()->drawComplexControl(QStyle::CC_Slider, &opt, &p, this);
343 bool QSlider::event(QEvent *event)
347 switch(event->type()) {
348 case QEvent::HoverEnter:
349 case QEvent::HoverLeave:
350 case QEvent::HoverMove:
351 if (const QHoverEvent *he = static_cast<const QHoverEvent *>(event))
352 d->updateHoverControl(he->pos());
354 case QEvent::StyleChange:
355 case QEvent::MacSizeChange:
356 d->resetLayoutItemMargins();
361 return QAbstractSlider::event(event);
367 void QSlider::mousePressEvent(QMouseEvent *ev)
370 if (d->maximum == d->minimum || (ev->buttons() ^ ev->button())) {
374 #ifdef QT_KEYPAD_NAVIGATION
375 if (QApplication::keypadNavigationEnabled())
379 if ((ev->button() & style()->styleHint(QStyle::SH_Slider_AbsoluteSetButtons)) == ev->button()) {
380 QStyleOptionSlider opt;
381 initStyleOption(&opt);
382 const QRect sliderRect = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
383 const QPoint center = sliderRect.center() - sliderRect.topLeft();
384 // to take half of the slider off for the setSliderPosition call we use the center - topLeft
386 setSliderPosition(d->pixelPosToRangeValue(d->pick(ev->pos() - center)));
387 triggerAction(SliderMove);
388 setRepeatAction(SliderNoAction);
389 d->pressedControl = QStyle::SC_SliderHandle;
391 } else if ((ev->button() & style()->styleHint(QStyle::SH_Slider_PageSetButtons)) == ev->button()) {
392 QStyleOptionSlider opt;
393 initStyleOption(&opt);
394 d->pressedControl = style()->hitTestComplexControl(QStyle::CC_Slider,
395 &opt, ev->pos(), this);
396 SliderAction action = SliderNoAction;
397 if (d->pressedControl == QStyle::SC_SliderGroove) {
398 const QRect sliderRect = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
399 int pressValue = d->pixelPosToRangeValue(d->pick(ev->pos() - sliderRect.center() + sliderRect.topLeft()));
400 d->pressValue = pressValue;
401 if (pressValue > d->value)
402 action = SliderPageStepAdd;
403 else if (pressValue < d->value)
404 action = SliderPageStepSub;
406 triggerAction(action);
407 setRepeatAction(action);
415 if (d->pressedControl == QStyle::SC_SliderHandle) {
416 QStyleOptionSlider opt;
417 initStyleOption(&opt);
418 setRepeatAction(SliderNoAction);
419 QRect sr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
420 d->clickOffset = d->pick(ev->pos() - sr.topLeft());
429 void QSlider::mouseMoveEvent(QMouseEvent *ev)
432 if (d->pressedControl != QStyle::SC_SliderHandle) {
437 int newPosition = d->pixelPosToRangeValue(d->pick(ev->pos()) - d->clickOffset);
438 QStyleOptionSlider opt;
439 initStyleOption(&opt);
440 setSliderPosition(newPosition);
447 void QSlider::mouseReleaseEvent(QMouseEvent *ev)
450 if (d->pressedControl == QStyle::SC_None || ev->buttons()) {
455 QStyle::SubControl oldPressed = QStyle::SubControl(d->pressedControl);
456 d->pressedControl = QStyle::SC_None;
457 setRepeatAction(SliderNoAction);
458 if (oldPressed == QStyle::SC_SliderHandle)
459 setSliderDown(false);
460 QStyleOptionSlider opt;
461 initStyleOption(&opt);
462 opt.subControls = oldPressed;
463 update(style()->subControlRect(QStyle::CC_Slider, &opt, oldPressed, this));
469 QSize QSlider::sizeHint() const
473 const int SliderLength = 84, TickSpace = 5;
474 QStyleOptionSlider opt;
475 initStyleOption(&opt);
476 int thick = style()->pixelMetric(QStyle::PM_SliderThickness, &opt, this);
477 if (d->tickPosition & TicksAbove)
479 if (d->tickPosition & TicksBelow)
481 int w = thick, h = SliderLength;
482 if (d->orientation == Qt::Horizontal) {
486 return style()->sizeFromContents(QStyle::CT_Slider, &opt, QSize(w, h), this).expandedTo(QApplication::globalStrut());
492 QSize QSlider::minimumSizeHint() const
495 QSize s = sizeHint();
496 QStyleOptionSlider opt;
497 initStyleOption(&opt);
498 int length = style()->pixelMetric(QStyle::PM_SliderLength, &opt, this);
499 if (d->orientation == Qt::Horizontal)
507 \property QSlider::tickPosition
508 \brief the tickmark position for this slider
510 The valid values are described by the QSlider::TickPosition enum.
512 The default value is \l QSlider::NoTicks.
517 void QSlider::setTickPosition(TickPosition position)
520 d->tickPosition = position;
521 d->resetLayoutItemMargins();
526 QSlider::TickPosition QSlider::tickPosition() const
528 return d_func()->tickPosition;
532 \property QSlider::tickInterval
533 \brief the interval between tickmarks
535 This is a value interval, not a pixel interval. If it is 0, the
536 slider will choose between singleStep and pageStep.
538 The default value is 0.
540 \sa tickPosition, singleStep, pageStep
543 void QSlider::setTickInterval(int ts)
545 d_func()->tickInterval = qMax(0, ts);
549 int QSlider::tickInterval() const
551 return d_func()->tickInterval;
554 Q_WIDGETS_EXPORT QStyleOptionSlider qt_qsliderStyleOption(QSlider *slider)
556 QStyleOptionSlider sliderOption;
557 slider->initStyleOption(&sliderOption);