Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qslider.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "qslider.h"
6#if QT_CONFIG(accessibility)
7#include "qaccessible.h"
8#endif
9#include "qapplication.h"
10#include "qevent.h"
11#include "qpainter.h"
12#include "qstyle.h"
13#include "qstyleoption.h"
14#include "qstylepainter.h"
15#include "private/qapplication_p.h"
16#include "private/qabstractslider_p.h"
17#include "qdebug.h"
18
20
22{
23 Q_DECLARE_PUBLIC(QSlider)
24public:
29 void init();
31 int pixelPosToRangeValue(int pos) const;
32 inline int pick(const QPoint &pt) const;
33
34 QStyle::SubControl newHoverControl(const QPoint &pos);
35 bool updateHoverControl(const QPoint &pos);
38};
39
41{
42 Q_Q(QSlider);
43 pressedControl = QStyle::SC_None;
44 tickInterval = 0;
45 tickPosition = QSlider::NoTicks;
46 hoverControl = QStyle::SC_None;
47 q->setFocusPolicy(Qt::FocusPolicy(q->style()->styleHint(QStyle::SH_Button_FocusPolicy, nullptr, q)));
48 QSizePolicy sp(QSizePolicy::Expanding, QSizePolicy::Fixed, QSizePolicy::Slider);
49 if (orientation == Qt::Vertical)
50 sp.transpose();
51 q->setSizePolicy(sp);
52 q->setAttribute(Qt::WA_WState_OwnSizePolicy, false);
54}
55
57{
58 Q_Q(QSlider);
59 QStyleOptionSlider opt;
60 // ### This is (also) reached from the ctor which is unfortunate since a possible
61 // ### re-implementation of initStyleOption is then not called.
62 q->initStyleOption(&opt);
63 setLayoutItemMargins(QStyle::SE_SliderLayoutItem, &opt);
64}
65
67{
68 Q_Q(const QSlider);
69 QStyleOptionSlider opt;
70 q->initStyleOption(&opt);
71 QRect gr = q->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, q);
72 QRect sr = q->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, q);
73 int sliderMin, sliderMax, sliderLength;
74
75 if (orientation == Qt::Horizontal) {
76 sliderLength = sr.width();
77 sliderMin = gr.x();
78 sliderMax = gr.right() - sliderLength + 1;
79 } else {
80 sliderLength = sr.height();
81 sliderMin = gr.y();
82 sliderMax = gr.bottom() - sliderLength + 1;
83 }
84 return QStyle::sliderValueFromPosition(minimum, maximum, pos - sliderMin,
85 sliderMax - sliderMin, opt.upsideDown);
86}
87
88inline int QSliderPrivate::pick(const QPoint &pt) const
89{
90 return orientation == Qt::Horizontal ? pt.x() : pt.y();
91}
92
93/*!
94 Initialize \a option with the values from this QSlider. This method
95 is useful for subclasses when they need a QStyleOptionSlider, but don't want
96 to fill in all the information themselves.
97
98 \sa QStyleOption::initFrom()
99*/
100void QSlider::initStyleOption(QStyleOptionSlider *option) const
101{
102 if (!option)
103 return;
104
105 Q_D(const QSlider);
106 option->initFrom(this);
107 option->subControls = QStyle::SC_None;
108 option->activeSubControls = QStyle::SC_None;
109 option->orientation = d->orientation;
110 option->maximum = d->maximum;
111 option->minimum = d->minimum;
112 option->tickPosition = (QSlider::TickPosition)d->tickPosition;
113 option->tickInterval = d->tickInterval;
114 option->upsideDown = (d->orientation == Qt::Horizontal) ?
115 (d->invertedAppearance != (option->direction == Qt::RightToLeft))
116 : (!d->invertedAppearance);
117 option->direction = Qt::LeftToRight; // we use the upsideDown option instead
118 option->sliderPosition = d->position;
119 option->sliderValue = d->value;
120 option->singleStep = d->singleStep;
121 option->pageStep = d->pageStep;
122 if (d->orientation == Qt::Horizontal)
123 option->state |= QStyle::State_Horizontal;
124
125 if (d->pressedControl) {
126 option->activeSubControls = d->pressedControl;
127 option->state |= QStyle::State_Sunken;
128 } else {
129 option->activeSubControls = d->hoverControl;
130 }
131}
132
133bool QSliderPrivate::updateHoverControl(const QPoint &pos)
134{
135 Q_Q(QSlider);
136 QRect lastHoverRect = hoverRect;
137 QStyle::SubControl lastHoverControl = hoverControl;
138 bool doesHover = q->testAttribute(Qt::WA_Hover);
139 if (lastHoverControl != newHoverControl(pos) && doesHover) {
140 q->update(lastHoverRect);
141 q->update(hoverRect);
142 return true;
143 }
144 return !doesHover;
145}
146
148{
149 Q_Q(QSlider);
150 QStyleOptionSlider opt;
151 q->initStyleOption(&opt);
152 opt.subControls = QStyle::SC_All;
153 QRect handleRect = q->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, q);
154 QRect grooveRect = q->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, q);
155 QRect tickmarksRect = q->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderTickmarks, q);
156
157 if (handleRect.contains(pos)) {
158 hoverRect = handleRect;
159 hoverControl = QStyle::SC_SliderHandle;
160 } else if (grooveRect.contains(pos)) {
161 hoverRect = grooveRect;
162 hoverControl = QStyle::SC_SliderGroove;
163 } else if (tickmarksRect.contains(pos)) {
164 hoverRect = tickmarksRect;
165 hoverControl = QStyle::SC_SliderTickmarks;
166 } else {
167 hoverRect = QRect();
168 hoverControl = QStyle::SC_None;
169 }
170
171 return hoverControl;
172}
173
174/*!
175 \class QSlider
176 \brief The QSlider widget provides a vertical or horizontal slider.
177
178 \ingroup basicwidgets
179 \inmodule QtWidgets
180
181 \image fusion-slider.png {Horizontal slider with tick marks}
182
183 The slider is the classic widget for controlling a bounded value.
184 It lets the user move a slider handle along a horizontal or vertical
185 groove and translates the handle's position into an integer value
186 within the legal range.
187
188 QSlider has very few of its own functions; most of the functionality is in
189 QAbstractSlider. The most useful functions are setValue() to set
190 the slider directly to some value; triggerAction() to simulate
191 the effects of clicking (useful for shortcut keys);
192 setSingleStep(), setPageStep() to set the steps; and setMinimum()
193 and setMaximum() to define the range of the scroll bar.
194
195 QSlider provides methods for controlling tickmarks. You can use
196 setTickPosition() to indicate where you want the tickmarks to be,
197 setTickInterval() to indicate how many of them you want. the
198 currently set tick position and interval can be queried using the
199 tickPosition() and tickInterval() functions, respectively.
200
201 QSlider inherits a comprehensive set of signals:
202 \table
203 \header \li Signal \li Description
204 \row \li \l valueChanged()
205 \li Emitted when the slider's value has changed. The tracking()
206 determines whether this signal is emitted during user
207 interaction.
208 \row \li \l sliderPressed()
209 \li Emitted when the user starts to drag the slider.
210 \row \li \l sliderMoved()
211 \li Emitted when the user drags the slider.
212 \row \li \l sliderReleased()
213 \li Emitted when the user releases the slider.
214 \endtable
215
216 QSlider only provides integer ranges. Note that although
217 QSlider handles very large numbers, it becomes difficult for users
218 to use a slider accurately for very large ranges.
219
220 A slider accepts focus on Tab and provides both a mouse wheel and a
221 keyboard interface. The keyboard interface is the following:
222
223 \list
224 \li Left/Right move a horizontal slider by one single step.
225 \li Up/Down move a vertical slider by one single step.
226 \li PageUp moves up one page.
227 \li PageDown moves down one page.
228 \li Home moves to the start (minimum).
229 \li End moves to the end (maximum).
230 \endlist
231
232 \sa QScrollBar, QSpinBox, QDial, {Sliders Example}
233*/
234
235
236/*!
237 \enum QSlider::TickPosition
238
239 This enum specifies where the tick marks are to be drawn relative
240 to the slider's groove and the handle the user moves.
241
242 \value NoTicks Do not draw any tick marks.
243 \value TicksBothSides Draw tick marks on both sides of the groove.
244 \value TicksAbove Draw tick marks above the (horizontal) slider
245 \value TicksBelow Draw tick marks below the (horizontal) slider
246 \value TicksLeft Draw tick marks to the left of the (vertical) slider
247 \value TicksRight Draw tick marks to the right of the (vertical) slider
248*/
249
250
251/*!
252 Constructs a vertical slider with the given \a parent.
253*/
254QSlider::QSlider(QWidget *parent)
255 : QSlider(Qt::Vertical, parent)
256{
257}
258
259/*!
260 Constructs a slider with the given \a parent. The \a orientation
261 parameter determines whether the slider is horizontal or vertical;
262 the valid values are Qt::Vertical and Qt::Horizontal.
263*/
264
265QSlider::QSlider(Qt::Orientation orientation, QWidget *parent)
266 : QAbstractSlider(*new QSliderPrivate, parent)
267{
268 d_func()->orientation = orientation;
269 d_func()->init();
270}
271
272
273/*!
274 Destroys this slider.
275*/
276QSlider::~QSlider()
277{
278}
279
280/*!
281 \reimp
282*/
283void QSlider::paintEvent(QPaintEvent *)
284{
285 Q_D(QSlider);
286 QStylePainter p(this);
287 QStyleOptionSlider opt;
288 initStyleOption(&opt);
289
290 opt.subControls = QStyle::SC_SliderGroove | QStyle::SC_SliderHandle;
291 if (d->tickPosition != NoTicks)
292 opt.subControls |= QStyle::SC_SliderTickmarks;
293
294 p.drawComplexControl(QStyle::CC_Slider, opt);
295}
296
297/*!
298 \reimp
299*/
300
301bool QSlider::event(QEvent *event)
302{
303 Q_D(QSlider);
304
305 switch(event->type()) {
306 case QEvent::HoverEnter:
307 case QEvent::HoverLeave:
308 case QEvent::HoverMove:
309 if (const QHoverEvent *he = static_cast<const QHoverEvent *>(event))
310 d->updateHoverControl(he->position().toPoint());
311 break;
312 case QEvent::StyleChange:
313 case QEvent::MacSizeChange:
314 d->resetLayoutItemMargins();
315 break;
316 default:
317 break;
318 }
319 return QAbstractSlider::event(event);
320}
321
322/*!
323 \reimp
324*/
325void QSlider::mousePressEvent(QMouseEvent *ev)
326{
327 Q_D(QSlider);
328 if (d->maximum == d->minimum || (ev->buttons() ^ ev->button())) {
329 ev->ignore();
330 return;
331 }
332#ifdef QT_KEYPAD_NAVIGATION
333 if (QApplicationPrivate::keypadNavigationEnabled())
334 setEditFocus(true);
335#endif
336 ev->accept();
337 if ((ev->button() & style()->styleHint(QStyle::SH_Slider_AbsoluteSetButtons, nullptr, this)) == ev->button()) {
338 QStyleOptionSlider opt;
339 initStyleOption(&opt);
340 const QRect sliderRect = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
341 const QPoint center = sliderRect.center() - sliderRect.topLeft();
342 // to take half of the slider off for the setSliderPosition call we use the center - topLeft
343
344 setSliderPosition(d->pixelPosToRangeValue(d->pick(ev->position().toPoint() - center)));
345 triggerAction(SliderMove);
346 setRepeatAction(SliderNoAction);
347 d->pressedControl = QStyle::SC_SliderHandle;
348 update();
349 } else if ((ev->button() & style()->styleHint(QStyle::SH_Slider_PageSetButtons, nullptr, this)) == ev->button()) {
350 QStyleOptionSlider opt;
351 initStyleOption(&opt);
352 d->pressedControl = style()->hitTestComplexControl(QStyle::CC_Slider,
353 &opt, ev->position().toPoint(), this);
354 SliderAction action = SliderNoAction;
355 if (d->pressedControl == QStyle::SC_SliderGroove) {
356 const QRect sliderRect = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
357 int pressValue = d->pixelPosToRangeValue(d->pick(ev->position().toPoint() - sliderRect.center() + sliderRect.topLeft()));
358 d->pressValue = pressValue;
359 if (pressValue > d->value)
360 action = SliderPageStepAdd;
361 else if (pressValue < d->value)
362 action = SliderPageStepSub;
363 if (action) {
364 triggerAction(action);
365 setRepeatAction(action);
366 }
367 }
368 } else {
369 ev->ignore();
370 return;
371 }
372
373 if (d->pressedControl == QStyle::SC_SliderHandle) {
374 QStyleOptionSlider opt;
375 initStyleOption(&opt);
376 setRepeatAction(SliderNoAction);
377 QRect sr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
378 d->clickOffset = d->pick(ev->position().toPoint() - sr.topLeft());
379 update(sr);
380 setSliderDown(true);
381 }
382}
383
384/*!
385 \reimp
386*/
387void QSlider::mouseMoveEvent(QMouseEvent *ev)
388{
389 Q_D(QSlider);
390 if (d->pressedControl != QStyle::SC_SliderHandle) {
391 ev->ignore();
392 return;
393 }
394 ev->accept();
395 int newPosition = d->pixelPosToRangeValue(d->pick(ev->position().toPoint()) - d->clickOffset);
396 setSliderPosition(newPosition);
397}
398
399
400/*!
401 \reimp
402*/
403void QSlider::mouseReleaseEvent(QMouseEvent *ev)
404{
405 Q_D(QSlider);
406 if (d->pressedControl == QStyle::SC_None || ev->buttons()) {
407 ev->ignore();
408 return;
409 }
410 ev->accept();
411 QStyle::SubControl oldPressed = QStyle::SubControl(d->pressedControl);
412 d->pressedControl = QStyle::SC_None;
413 setRepeatAction(SliderNoAction);
414 if (oldPressed == QStyle::SC_SliderHandle)
415 setSliderDown(false);
416 QStyleOptionSlider opt;
417 initStyleOption(&opt);
418 opt.subControls = oldPressed;
419 update(style()->subControlRect(QStyle::CC_Slider, &opt, oldPressed, this));
420}
421
422/*!
423 \reimp
424*/
425QSize QSlider::sizeHint() const
426{
427 Q_D(const QSlider);
428 ensurePolished();
429 const int SliderLength = 84, TickSpace = 5;
430 QStyleOptionSlider opt;
431 initStyleOption(&opt);
432 int thick = style()->pixelMetric(QStyle::PM_SliderThickness, &opt, this);
433 if (d->tickPosition & TicksAbove)
434 thick += TickSpace;
435 if (d->tickPosition & TicksBelow)
436 thick += TickSpace;
437 int w = thick, h = SliderLength;
438 if (d->orientation == Qt::Horizontal) {
439 w = SliderLength;
440 h = thick;
441 }
442 return style()->sizeFromContents(QStyle::CT_Slider, &opt, QSize(w, h), this);
443}
444
445/*!
446 \reimp
447*/
448QSize QSlider::minimumSizeHint() const
449{
450 Q_D(const QSlider);
451 QSize s = sizeHint();
452 QStyleOptionSlider opt;
453 initStyleOption(&opt);
454 int length = style()->pixelMetric(QStyle::PM_SliderLength, &opt, this);
455 if (d->orientation == Qt::Horizontal)
456 s.setWidth(length);
457 else
458 s.setHeight(length);
459 return s;
460}
461
462/*!
463 \property QSlider::tickPosition
464 \brief the tickmark position for this slider
465
466 The valid values are described by the QSlider::TickPosition enum.
467
468 The default value is \l QSlider::NoTicks.
469
470 \sa tickInterval
471*/
472
473void QSlider::setTickPosition(TickPosition position)
474{
475 Q_D(QSlider);
476 d->tickPosition = position;
477 d->resetLayoutItemMargins();
478 update();
479 updateGeometry();
480}
481
482QSlider::TickPosition QSlider::tickPosition() const
483{
484 return d_func()->tickPosition;
485}
486
487/*!
488 \property QSlider::tickInterval
489 \brief the interval between tickmarks
490
491 This is a value interval, not a pixel interval. If it is 0, the
492 slider will choose between singleStep and pageStep.
493
494 The default value is 0.
495
496 \sa tickPosition, singleStep, pageStep
497*/
498
499void QSlider::setTickInterval(int ts)
500{
501 d_func()->tickInterval = qMax(0, ts);
502 update();
503}
504
505int QSlider::tickInterval() const
506{
507 return d_func()->tickInterval;
508}
509
510Q_WIDGETS_EXPORT QStyleOptionSlider qt_qsliderStyleOption(QSlider *slider)
511{
512 QStyleOptionSlider sliderOption;
513 slider->initStyleOption(&sliderOption);
514 return sliderOption;
515}
516
517QT_END_NAMESPACE
518
519#include "moc_qslider.cpp"
QStyle::SubControl hoverControl
Definition qslider.cpp:36
int pixelPosToRangeValue(int pos) const
Definition qslider.cpp:66
QStyle::SubControl newHoverControl(const QPoint &pos)
Definition qslider.cpp:147
int pick(const QPoint &pt) const
Definition qslider.cpp:88
void resetLayoutItemMargins()
Definition qslider.cpp:56
bool updateHoverControl(const QPoint &pos)
Definition qslider.cpp:133
QRect hoverRect
Definition qslider.cpp:37