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 ev->accept();
333 if ((ev->button() & style()->styleHint(QStyle::SH_Slider_AbsoluteSetButtons, nullptr, this)) == ev->button()) {
334 QStyleOptionSlider opt;
335 initStyleOption(&opt);
336 const QRect sliderRect = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
337 const QPoint center = sliderRect.center() - sliderRect.topLeft();
338 // to take half of the slider off for the setSliderPosition call we use the center - topLeft
339
340 setSliderPosition(d->pixelPosToRangeValue(d->pick(ev->position().toPoint() - center)));
341 triggerAction(SliderMove);
342 setRepeatAction(SliderNoAction);
343 d->pressedControl = QStyle::SC_SliderHandle;
344 update();
345 } else if ((ev->button() & style()->styleHint(QStyle::SH_Slider_PageSetButtons, nullptr, this)) == ev->button()) {
346 QStyleOptionSlider opt;
347 initStyleOption(&opt);
348 d->pressedControl = style()->hitTestComplexControl(QStyle::CC_Slider,
349 &opt, ev->position().toPoint(), this);
350 SliderAction action = SliderNoAction;
351 if (d->pressedControl == QStyle::SC_SliderGroove) {
352 const QRect sliderRect = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
353 int pressValue = d->pixelPosToRangeValue(d->pick(ev->position().toPoint() - sliderRect.center() + sliderRect.topLeft()));
354 d->pressValue = pressValue;
355 if (pressValue > d->value)
356 action = SliderPageStepAdd;
357 else if (pressValue < d->value)
358 action = SliderPageStepSub;
359 if (action) {
360 triggerAction(action);
361 setRepeatAction(action);
362 }
363 }
364 } else {
365 ev->ignore();
366 return;
367 }
368
369 if (d->pressedControl == QStyle::SC_SliderHandle) {
370 QStyleOptionSlider opt;
371 initStyleOption(&opt);
372 setRepeatAction(SliderNoAction);
373 QRect sr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
374 d->clickOffset = d->pick(ev->position().toPoint() - sr.topLeft());
375 update(sr);
376 setSliderDown(true);
377 }
378}
379
380/*!
381 \reimp
382*/
383void QSlider::mouseMoveEvent(QMouseEvent *ev)
384{
385 Q_D(QSlider);
386 if (d->pressedControl != QStyle::SC_SliderHandle) {
387 ev->ignore();
388 return;
389 }
390 ev->accept();
391 int newPosition = d->pixelPosToRangeValue(d->pick(ev->position().toPoint()) - d->clickOffset);
392 setSliderPosition(newPosition);
393}
394
395
396/*!
397 \reimp
398*/
399void QSlider::mouseReleaseEvent(QMouseEvent *ev)
400{
401 Q_D(QSlider);
402 if (d->pressedControl == QStyle::SC_None || ev->buttons()) {
403 ev->ignore();
404 return;
405 }
406 ev->accept();
407 QStyle::SubControl oldPressed = QStyle::SubControl(d->pressedControl);
408 d->pressedControl = QStyle::SC_None;
409 setRepeatAction(SliderNoAction);
410 if (oldPressed == QStyle::SC_SliderHandle)
411 setSliderDown(false);
412 QStyleOptionSlider opt;
413 initStyleOption(&opt);
414 opt.subControls = oldPressed;
415 update(style()->subControlRect(QStyle::CC_Slider, &opt, oldPressed, this));
416}
417
418/*!
419 \reimp
420*/
421QSize QSlider::sizeHint() const
422{
423 Q_D(const QSlider);
424 ensurePolished();
425 const int SliderLength = 84, TickSpace = 5;
426 QStyleOptionSlider opt;
427 initStyleOption(&opt);
428 int thick = style()->pixelMetric(QStyle::PM_SliderThickness, &opt, this);
429 if (d->tickPosition & TicksAbove)
430 thick += TickSpace;
431 if (d->tickPosition & TicksBelow)
432 thick += TickSpace;
433 int w = thick, h = SliderLength;
434 if (d->orientation == Qt::Horizontal) {
435 w = SliderLength;
436 h = thick;
437 }
438 return style()->sizeFromContents(QStyle::CT_Slider, &opt, QSize(w, h), this);
439}
440
441/*!
442 \reimp
443*/
444QSize QSlider::minimumSizeHint() const
445{
446 Q_D(const QSlider);
447 QSize s = sizeHint();
448 QStyleOptionSlider opt;
449 initStyleOption(&opt);
450 int length = style()->pixelMetric(QStyle::PM_SliderLength, &opt, this);
451 if (d->orientation == Qt::Horizontal)
452 s.setWidth(length);
453 else
454 s.setHeight(length);
455 return s;
456}
457
458/*!
459 \property QSlider::tickPosition
460 \brief the tickmark position for this slider
461
462 The valid values are described by the QSlider::TickPosition enum.
463
464 The default value is \l QSlider::NoTicks.
465
466 \sa tickInterval
467*/
468
469void QSlider::setTickPosition(TickPosition position)
470{
471 Q_D(QSlider);
472 d->tickPosition = position;
473 d->resetLayoutItemMargins();
474 update();
475 updateGeometry();
476}
477
478QSlider::TickPosition QSlider::tickPosition() const
479{
480 return d_func()->tickPosition;
481}
482
483/*!
484 \property QSlider::tickInterval
485 \brief the interval between tickmarks
486
487 This is a value interval, not a pixel interval. If it is \c 0, the
488 slider will choose between singleStep and pageStep.
489
490 The default is \c 0. Negative values are invalid and therefore
491 clamped to \c 0.
492
493 \sa tickPosition, singleStep, pageStep
494*/
495
496void QSlider::setTickInterval(int ts)
497{
498 d_func()->tickInterval = qMax(0, ts);
499 update();
500}
501
502int QSlider::tickInterval() const
503{
504 return d_func()->tickInterval;
505}
506
507Q_WIDGETS_EXPORT QStyleOptionSlider qt_qsliderStyleOption(QSlider *slider)
508{
509 QStyleOptionSlider sliderOption;
510 slider->initStyleOption(&sliderOption);
511 return sliderOption;
512}
513
514QT_END_NAMESPACE
515
516#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
Combined button and popup list for selecting options.