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 | |
4 | #include <qapplication.h> |
5 | #include "qabstractslider.h" |
6 | #include "qevent.h" |
7 | #include "qabstractslider_p.h" |
8 | #include "qdebug.h" |
9 | #if QT_CONFIG(accessibility) |
10 | #include "qaccessible.h" |
11 | #endif |
12 | #include <limits.h> |
13 | |
14 | #include <private/qapplication_p.h> |
15 | |
16 | QT_BEGIN_NAMESPACE |
17 | |
18 | /*! |
19 | \class QAbstractSlider |
20 | \brief The QAbstractSlider class provides an integer value within a range. |
21 | |
22 | \ingroup abstractwidgets |
23 | \inmodule QtWidgets |
24 | |
25 | The class is designed as a common super class for widgets like |
26 | QScrollBar, QSlider and QDial. |
27 | |
28 | Here are the main properties of the class: |
29 | |
30 | \list 1 |
31 | |
32 | \li \l value: The bounded integer that QAbstractSlider maintains. |
33 | |
34 | \li \l minimum: The lowest possible value. |
35 | |
36 | \li \l maximum: The highest possible value. |
37 | |
38 | \li \l singleStep: The smaller of two natural steps that an |
39 | abstract sliders provides and typically corresponds to the user |
40 | pressing an arrow key. |
41 | |
42 | \li \l pageStep: The larger of two natural steps that an abstract |
43 | slider provides and typically corresponds to the user pressing |
44 | PageUp or PageDown. |
45 | |
46 | \li \l tracking: Whether slider tracking is enabled. |
47 | |
48 | \li \l sliderPosition: The current position of the slider. If \l |
49 | tracking is enabled (the default), this is identical to \l value. |
50 | |
51 | \endlist |
52 | |
53 | Unity (1) may be viewed as a third step size. setValue() lets you |
54 | set the current value to any integer in the allowed range, not |
55 | just minimum() + \e n * singleStep() for integer values of \e n. |
56 | Some widgets may allow the user to set any value at all; others |
57 | may just provide multiples of singleStep() or pageStep(). |
58 | |
59 | QAbstractSlider emits a comprehensive set of signals: |
60 | |
61 | \table |
62 | \header \li Signal \li Emitted when |
63 | \row \li \l valueChanged() |
64 | \li the value has changed. The \l tracking |
65 | determines whether this signal is emitted during user |
66 | interaction. |
67 | \row \li \l sliderPressed() |
68 | \li the user starts to drag the slider. |
69 | \row \li \l sliderMoved() |
70 | \li the user drags the slider. |
71 | \row \li \l sliderReleased() |
72 | \li the user releases the slider. |
73 | \row \li \l actionTriggered() |
74 | \li a slider action was triggered. |
75 | \row \li \l rangeChanged() |
76 | \li a the range has changed. |
77 | \endtable |
78 | |
79 | QAbstractSlider provides a virtual sliderChange() function that is |
80 | well suited for updating the on-screen representation of |
81 | sliders. By calling triggerAction(), subclasses trigger slider |
82 | actions. Two helper functions QStyle::sliderPositionFromValue() and |
83 | QStyle::sliderValueFromPosition() help subclasses and styles to map |
84 | screen coordinates to logical range values. |
85 | |
86 | \sa QAbstractSpinBox, QSlider, QDial, QScrollBar, {Sliders Example} |
87 | */ |
88 | |
89 | /*! |
90 | \enum QAbstractSlider::SliderAction |
91 | |
92 | \value SliderNoAction |
93 | \value SliderSingleStepAdd |
94 | \value SliderSingleStepSub |
95 | \value SliderPageStepAdd |
96 | \value SliderPageStepSub |
97 | \value SliderToMinimum |
98 | \value SliderToMaximum |
99 | \value SliderMove |
100 | |
101 | */ |
102 | |
103 | /*! |
104 | \fn void QAbstractSlider::valueChanged(int value) |
105 | |
106 | This signal is emitted when the slider value has changed, with the |
107 | new slider \a value as argument. |
108 | */ |
109 | |
110 | /*! |
111 | \fn void QAbstractSlider::sliderPressed() |
112 | |
113 | This signal is emitted when the user presses the slider with the |
114 | mouse, or programmatically when setSliderDown(true) is called. |
115 | |
116 | \sa sliderReleased(), sliderMoved(), isSliderDown() |
117 | */ |
118 | |
119 | /*! |
120 | \fn void QAbstractSlider::sliderMoved(int value) |
121 | |
122 | This signal is emitted when sliderDown is true and the slider moves. This |
123 | usually happens when the user is dragging the slider. The \a value |
124 | is the new slider position. |
125 | |
126 | This signal is emitted even when tracking is turned off. |
127 | |
128 | \sa setTracking(), valueChanged(), isSliderDown(), |
129 | sliderPressed(), sliderReleased() |
130 | */ |
131 | |
132 | /*! |
133 | \fn void QAbstractSlider::sliderReleased() |
134 | |
135 | This signal is emitted when the user releases the slider with the |
136 | mouse, or programmatically when setSliderDown(false) is called. |
137 | |
138 | \sa sliderPressed(), sliderMoved(), sliderDown |
139 | */ |
140 | |
141 | /*! |
142 | \fn void QAbstractSlider::rangeChanged(int min, int max) |
143 | |
144 | This signal is emitted when the slider range has changed, with \a |
145 | min being the new minimum, and \a max being the new maximum. |
146 | |
147 | \sa minimum, maximum |
148 | */ |
149 | |
150 | /*! |
151 | \fn void QAbstractSlider::actionTriggered(int action) |
152 | |
153 | This signal is emitted when the slider action \a action is |
154 | triggered. Actions are \l SliderSingleStepAdd, \l |
155 | SliderSingleStepSub, \l SliderPageStepAdd, \l SliderPageStepSub, |
156 | \l SliderToMinimum, \l SliderToMaximum, and \l SliderMove. |
157 | |
158 | When the signal is emitted, the \l sliderPosition has been |
159 | adjusted according to the action, but the \l value has not yet |
160 | been propagated (meaning the valueChanged() signal was not yet |
161 | emitted), and the visual display has not been updated. In slots |
162 | connected to this signal you can thus safely adjust any action by |
163 | calling setSliderPosition() yourself, based on both the action and |
164 | the slider's value. |
165 | |
166 | \sa triggerAction() |
167 | */ |
168 | |
169 | /*! |
170 | \enum QAbstractSlider::SliderChange |
171 | |
172 | \value SliderRangeChange |
173 | \value SliderOrientationChange |
174 | \value SliderStepsChange |
175 | \value SliderValueChange |
176 | */ |
177 | |
178 | QAbstractSliderPrivate::QAbstractSliderPrivate() |
179 | : minimum(0), maximum(99), pageStep(10), value(0), position(0), pressValue(-1), |
180 | singleStep(1), singleStepFromItemView(-1), viewMayChangeSingleStep(true), offset_accumulated(0), tracking(true), |
181 | blocktracking(false), pressed(false), |
182 | invertedAppearance(false), invertedControls(false), |
183 | orientation(Qt::Horizontal), repeatAction(QAbstractSlider::SliderNoAction) |
184 | #ifdef QT_KEYPAD_NAVIGATION |
185 | , isAutoRepeating(false) |
186 | , repeatMultiplier(1) |
187 | { |
188 | firstRepeat.invalidate(); |
189 | #else |
190 | { |
191 | #endif |
192 | |
193 | } |
194 | |
195 | QAbstractSliderPrivate::~QAbstractSliderPrivate() |
196 | { |
197 | } |
198 | |
199 | /*! |
200 | Sets the slider's minimum to \a min and its maximum to \a max. |
201 | |
202 | If \a max is smaller than \a min, \a min becomes the only legal |
203 | value. |
204 | |
205 | \sa minimum, maximum |
206 | */ |
207 | void QAbstractSlider::setRange(int min, int max) |
208 | { |
209 | Q_D(QAbstractSlider); |
210 | int oldMin = d->minimum; |
211 | int oldMax = d->maximum; |
212 | d->minimum = min; |
213 | d->maximum = qMax(a: min, b: max); |
214 | if (oldMin != d->minimum || oldMax != d->maximum) { |
215 | sliderChange(change: SliderRangeChange); |
216 | emit rangeChanged(min: d->minimum, max: d->maximum); |
217 | setValue(d->value); // re-bound |
218 | } |
219 | } |
220 | |
221 | |
222 | void QAbstractSliderPrivate::setSteps(int single, int page) |
223 | { |
224 | Q_Q(QAbstractSlider); |
225 | singleStep = qAbs(t: single); |
226 | pageStep = qAbs(t: page); |
227 | q->sliderChange(change: QAbstractSlider::SliderStepsChange); |
228 | } |
229 | |
230 | /*! |
231 | Constructs an abstract slider. |
232 | |
233 | The \a parent argument is sent to the QWidget constructor. |
234 | |
235 | The \l minimum defaults to 0, the \l maximum to 99, with a \l |
236 | singleStep size of 1 and a \l pageStep size of 10, and an initial |
237 | \l value of 0. |
238 | */ |
239 | QAbstractSlider::QAbstractSlider(QWidget *parent) |
240 | :QWidget(*new QAbstractSliderPrivate, parent, { }) |
241 | { |
242 | } |
243 | |
244 | /*! \internal */ |
245 | QAbstractSlider::QAbstractSlider(QAbstractSliderPrivate &dd, QWidget *parent) |
246 | :QWidget(dd, parent, { }) |
247 | { |
248 | } |
249 | |
250 | /*! |
251 | Destroys the slider. |
252 | */ |
253 | QAbstractSlider::~QAbstractSlider() |
254 | { |
255 | } |
256 | |
257 | /*! |
258 | \property QAbstractSlider::orientation |
259 | \brief the orientation of the slider |
260 | |
261 | The orientation must be \l Qt::Vertical (the default) or \l |
262 | Qt::Horizontal. |
263 | */ |
264 | void QAbstractSlider::setOrientation(Qt::Orientation orientation) |
265 | { |
266 | Q_D(QAbstractSlider); |
267 | if (d->orientation == orientation) |
268 | return; |
269 | |
270 | d->orientation = orientation; |
271 | if (!testAttribute(attribute: Qt::WA_WState_OwnSizePolicy)) { |
272 | setSizePolicy(sizePolicy().transposed()); |
273 | setAttribute(Qt::WA_WState_OwnSizePolicy, on: false); |
274 | } |
275 | update(); |
276 | updateGeometry(); |
277 | } |
278 | |
279 | Qt::Orientation QAbstractSlider::orientation() const |
280 | { |
281 | Q_D(const QAbstractSlider); |
282 | return d->orientation; |
283 | } |
284 | |
285 | |
286 | /*! |
287 | \property QAbstractSlider::minimum |
288 | \brief the sliders's minimum value |
289 | |
290 | When setting this property, the \l maximum is adjusted if |
291 | necessary to ensure that the range remains valid. Also the |
292 | slider's current value is adjusted to be within the new range. |
293 | |
294 | */ |
295 | |
296 | void QAbstractSlider::setMinimum(int min) |
297 | { |
298 | Q_D(QAbstractSlider); |
299 | setRange(min, max: qMax(a: d->maximum, b: min)); |
300 | } |
301 | |
302 | int QAbstractSlider::minimum() const |
303 | { |
304 | Q_D(const QAbstractSlider); |
305 | return d->minimum; |
306 | } |
307 | |
308 | |
309 | /*! |
310 | \property QAbstractSlider::maximum |
311 | \brief the slider's maximum value |
312 | |
313 | When setting this property, the \l minimum is adjusted if |
314 | necessary to ensure that the range remains valid. Also the |
315 | slider's current value is adjusted to be within the new range. |
316 | |
317 | |
318 | */ |
319 | |
320 | void QAbstractSlider::setMaximum(int max) |
321 | { |
322 | Q_D(QAbstractSlider); |
323 | setRange(min: qMin(a: d->minimum, b: max), max); |
324 | } |
325 | |
326 | int QAbstractSlider::maximum() const |
327 | { |
328 | Q_D(const QAbstractSlider); |
329 | return d->maximum; |
330 | } |
331 | |
332 | |
333 | |
334 | /*! |
335 | \property QAbstractSlider::singleStep |
336 | \brief the single step. |
337 | |
338 | The smaller of two natural steps that an |
339 | abstract sliders provides and typically corresponds to the user |
340 | pressing an arrow key. |
341 | |
342 | If the property is modified during an auto repeating key event, behavior |
343 | is undefined. |
344 | |
345 | \sa pageStep |
346 | */ |
347 | |
348 | void QAbstractSlider::setSingleStep(int step) |
349 | { |
350 | Q_D(QAbstractSlider); |
351 | |
352 | d->viewMayChangeSingleStep = (step < 0); |
353 | if (step < 0 && d->singleStepFromItemView > 0) |
354 | step = d->singleStepFromItemView; |
355 | |
356 | if (step != d->singleStep) |
357 | d->setSteps(single: step, page: d->pageStep); |
358 | } |
359 | |
360 | int QAbstractSlider::singleStep() const |
361 | { |
362 | Q_D(const QAbstractSlider); |
363 | return d->singleStep; |
364 | } |
365 | |
366 | |
367 | /*! |
368 | \property QAbstractSlider::pageStep |
369 | \brief the page step. |
370 | |
371 | The larger of two natural steps that an abstract slider provides |
372 | and typically corresponds to the user pressing PageUp or PageDown. |
373 | |
374 | \sa singleStep |
375 | */ |
376 | |
377 | void QAbstractSlider::setPageStep(int step) |
378 | { |
379 | Q_D(QAbstractSlider); |
380 | if (step != d->pageStep) |
381 | d->setSteps(single: d->singleStep, page: step); |
382 | } |
383 | |
384 | int QAbstractSlider::pageStep() const |
385 | { |
386 | Q_D(const QAbstractSlider); |
387 | return d->pageStep; |
388 | } |
389 | |
390 | /*! |
391 | \property QAbstractSlider::tracking |
392 | \brief whether slider tracking is enabled |
393 | |
394 | If tracking is enabled (the default), the slider emits the |
395 | valueChanged() signal while the slider is being dragged. If |
396 | tracking is disabled, the slider emits the valueChanged() signal |
397 | only when the user releases the slider. |
398 | |
399 | \sa sliderDown |
400 | */ |
401 | void QAbstractSlider::setTracking(bool enable) |
402 | { |
403 | Q_D(QAbstractSlider); |
404 | d->tracking = enable; |
405 | } |
406 | |
407 | bool QAbstractSlider::hasTracking() const |
408 | { |
409 | Q_D(const QAbstractSlider); |
410 | return d->tracking; |
411 | } |
412 | |
413 | |
414 | /*! |
415 | \property QAbstractSlider::sliderDown |
416 | \brief whether the slider is pressed down. |
417 | |
418 | The property is set by subclasses in order to let the abstract |
419 | slider know whether or not \l tracking has any effect. |
420 | |
421 | Changing the slider down property emits the sliderPressed() and |
422 | sliderReleased() signals. |
423 | |
424 | */ |
425 | void QAbstractSlider::setSliderDown(bool down) |
426 | { |
427 | Q_D(QAbstractSlider); |
428 | bool doEmit = d->pressed != down; |
429 | |
430 | d->pressed = down; |
431 | |
432 | if (doEmit) { |
433 | if (down) |
434 | emit sliderPressed(); |
435 | else |
436 | emit sliderReleased(); |
437 | } |
438 | |
439 | if (!down && d->position != d->value) |
440 | triggerAction(action: SliderMove); |
441 | } |
442 | |
443 | bool QAbstractSlider::isSliderDown() const |
444 | { |
445 | Q_D(const QAbstractSlider); |
446 | return d->pressed; |
447 | } |
448 | |
449 | |
450 | /*! |
451 | \property QAbstractSlider::sliderPosition |
452 | \brief the current slider position |
453 | |
454 | If \l tracking is enabled (the default), this is identical to \l value. |
455 | */ |
456 | void QAbstractSlider::setSliderPosition(int position) |
457 | { |
458 | Q_D(QAbstractSlider); |
459 | position = d->bound(val: position); |
460 | if (position == d->position) |
461 | return; |
462 | d->position = position; |
463 | if (!d->tracking) |
464 | update(); |
465 | if (d->pressed) |
466 | emit sliderMoved(position); |
467 | if (d->tracking && !d->blocktracking) |
468 | triggerAction(action: SliderMove); |
469 | } |
470 | |
471 | int QAbstractSlider::sliderPosition() const |
472 | { |
473 | Q_D(const QAbstractSlider); |
474 | return d->position; |
475 | } |
476 | |
477 | |
478 | /*! |
479 | \property QAbstractSlider::value |
480 | \brief the slider's current value |
481 | |
482 | The slider forces the value to be within the legal range: \l |
483 | minimum <= \c value <= \l maximum. |
484 | |
485 | Changing the value also changes the \l sliderPosition. |
486 | */ |
487 | |
488 | |
489 | int QAbstractSlider::value() const |
490 | { |
491 | Q_D(const QAbstractSlider); |
492 | return d->value; |
493 | } |
494 | |
495 | void QAbstractSlider::setValue(int value) |
496 | { |
497 | Q_D(QAbstractSlider); |
498 | value = d->bound(val: value); |
499 | if (d->value == value && d->position == value) |
500 | return; |
501 | |
502 | // delay signal emission until sliderChanged() has been called |
503 | const bool emitValueChanged = (value != d->value); |
504 | d->value = value; |
505 | |
506 | if (d->position != value) { |
507 | d->position = value; |
508 | if (d->pressed) |
509 | emit sliderMoved(position: d->position); |
510 | } |
511 | #if QT_CONFIG(accessibility) |
512 | QAccessibleValueChangeEvent event(this, d->value); |
513 | QAccessible::updateAccessibility(event: &event); |
514 | #endif |
515 | sliderChange(change: SliderValueChange); |
516 | |
517 | if (emitValueChanged) |
518 | emit valueChanged(value); |
519 | |
520 | } |
521 | |
522 | /*! |
523 | \property QAbstractSlider::invertedAppearance |
524 | \brief whether or not a slider shows its values inverted. |
525 | |
526 | If this property is \c false (the default), the minimum and maximum will |
527 | be shown in its classic position for the inherited widget. If the |
528 | value is true, the minimum and maximum appear at their opposite location. |
529 | |
530 | Note: This property makes most sense for sliders and dials. For |
531 | scroll bars, the visual effect of the scroll bar subcontrols depends on |
532 | whether or not the styles understand inverted appearance; most styles |
533 | ignore this property for scroll bars. |
534 | */ |
535 | |
536 | bool QAbstractSlider::invertedAppearance() const |
537 | { |
538 | Q_D(const QAbstractSlider); |
539 | return d->invertedAppearance; |
540 | } |
541 | |
542 | void QAbstractSlider::setInvertedAppearance(bool invert) |
543 | { |
544 | Q_D(QAbstractSlider); |
545 | d->invertedAppearance = invert; |
546 | update(); |
547 | } |
548 | |
549 | |
550 | /*! |
551 | \property QAbstractSlider::invertedControls |
552 | \brief whether or not the slider inverts its wheel and key events. |
553 | |
554 | If this property is \c false, scrolling the mouse wheel "up" and using keys |
555 | like page up will increase the slider's value towards its maximum. Otherwise |
556 | pressing page up will move value towards the slider's minimum. |
557 | */ |
558 | |
559 | |
560 | bool QAbstractSlider::invertedControls() const |
561 | { |
562 | Q_D(const QAbstractSlider); |
563 | return d->invertedControls; |
564 | } |
565 | |
566 | void QAbstractSlider::setInvertedControls(bool invert) |
567 | { |
568 | Q_D(QAbstractSlider); |
569 | d->invertedControls = invert; |
570 | } |
571 | |
572 | /*! Triggers a slider \a action. Possible actions are \l |
573 | SliderSingleStepAdd, \l SliderSingleStepSub, \l SliderPageStepAdd, |
574 | \l SliderPageStepSub, \l SliderToMinimum, \l SliderToMaximum, and \l |
575 | SliderMove. |
576 | |
577 | \sa actionTriggered() |
578 | */ |
579 | void QAbstractSlider::triggerAction(SliderAction action) |
580 | { |
581 | Q_D(QAbstractSlider); |
582 | d->blocktracking = true; |
583 | switch (action) { |
584 | case SliderSingleStepAdd: |
585 | setSliderPosition(d->overflowSafeAdd(add: d->effectiveSingleStep())); |
586 | break; |
587 | case SliderSingleStepSub: |
588 | setSliderPosition(d->overflowSafeAdd(add: -d->effectiveSingleStep())); |
589 | break; |
590 | case SliderPageStepAdd: |
591 | setSliderPosition(d->overflowSafeAdd(add: d->pageStep)); |
592 | break; |
593 | case SliderPageStepSub: |
594 | setSliderPosition(d->overflowSafeAdd(add: -d->pageStep)); |
595 | break; |
596 | case SliderToMinimum: |
597 | setSliderPosition(d->minimum); |
598 | break; |
599 | case SliderToMaximum: |
600 | setSliderPosition(d->maximum); |
601 | break; |
602 | case SliderMove: |
603 | case SliderNoAction: |
604 | break; |
605 | }; |
606 | emit actionTriggered(action); |
607 | d->blocktracking = false; |
608 | setValue(d->position); |
609 | } |
610 | |
611 | /*! Sets action \a action to be triggered repetitively in intervals |
612 | of \a repeatTime, after an initial delay of \a thresholdTime. |
613 | |
614 | \sa triggerAction(), repeatAction() |
615 | */ |
616 | void QAbstractSlider::setRepeatAction(SliderAction action, int thresholdTime, int repeatTime) |
617 | { |
618 | Q_D(QAbstractSlider); |
619 | if ((d->repeatAction = action) == SliderNoAction) { |
620 | d->repeatActionTimer.stop(); |
621 | } else { |
622 | d->repeatActionTime = repeatTime; |
623 | d->repeatActionTimer.start(msec: thresholdTime, obj: this); |
624 | } |
625 | } |
626 | |
627 | /*! |
628 | Returns the current repeat action. |
629 | \sa setRepeatAction() |
630 | */ |
631 | QAbstractSlider::SliderAction QAbstractSlider::repeatAction() const |
632 | { |
633 | Q_D(const QAbstractSlider); |
634 | return d->repeatAction; |
635 | } |
636 | |
637 | /*!\reimp |
638 | */ |
639 | void QAbstractSlider::timerEvent(QTimerEvent *e) |
640 | { |
641 | Q_D(QAbstractSlider); |
642 | if (e->timerId() == d->repeatActionTimer.timerId()) { |
643 | if (d->repeatActionTime) { // was threshold time, use repeat time next time |
644 | d->repeatActionTimer.start(msec: d->repeatActionTime, obj: this); |
645 | d->repeatActionTime = 0; |
646 | } |
647 | if (d->repeatAction == SliderPageStepAdd) |
648 | d->setAdjustedSliderPosition(d->overflowSafeAdd(add: d->pageStep)); |
649 | else if (d->repeatAction == SliderPageStepSub) |
650 | d->setAdjustedSliderPosition(d->overflowSafeAdd(add: -d->pageStep)); |
651 | else |
652 | triggerAction(action: d->repeatAction); |
653 | } |
654 | } |
655 | |
656 | /*! |
657 | Reimplement this virtual function to track slider changes such as |
658 | \l SliderRangeChange, \l SliderOrientationChange, \l |
659 | SliderStepsChange, or \l SliderValueChange. The default |
660 | implementation only updates the display and ignores the \a change |
661 | parameter. |
662 | */ |
663 | void QAbstractSlider::sliderChange(SliderChange) |
664 | { |
665 | update(); |
666 | } |
667 | |
668 | bool QAbstractSliderPrivate::scrollByDelta(Qt::Orientation orientation, Qt::KeyboardModifiers modifiers, int delta) |
669 | { |
670 | Q_Q(QAbstractSlider); |
671 | int stepsToScroll = 0; |
672 | // in Qt scrolling to the right gives negative values. |
673 | if (orientation == Qt::Horizontal) |
674 | delta = -delta; |
675 | qreal offset = qreal(delta) / 120; |
676 | |
677 | if ((modifiers & Qt::ControlModifier) || (modifiers & Qt::ShiftModifier)) { |
678 | // Scroll one page regardless of delta: |
679 | stepsToScroll = qBound(min: -pageStep, val: int(offset * pageStep), max: pageStep); |
680 | offset_accumulated = 0; |
681 | } else { |
682 | // Calculate how many lines to scroll. Depending on what delta is (and |
683 | // offset), we might end up with a fraction (e.g. scroll 1.3 lines). We can |
684 | // only scroll whole lines, so we keep the reminder until next event. |
685 | qreal stepsToScrollF = |
686 | #if QT_CONFIG(wheelevent) |
687 | QApplication::wheelScrollLines() * |
688 | #endif |
689 | offset * effectiveSingleStep(); |
690 | // Check if wheel changed direction since last event: |
691 | if (offset_accumulated != 0 && (offset / offset_accumulated) < 0) |
692 | offset_accumulated = 0; |
693 | |
694 | offset_accumulated += stepsToScrollF; |
695 | |
696 | // Don't scroll more than one page in any case: |
697 | stepsToScroll = qBound(min: -pageStep, val: int(offset_accumulated), max: pageStep); |
698 | |
699 | offset_accumulated -= int(offset_accumulated); |
700 | if (stepsToScroll == 0) { |
701 | // We moved less than a line, but might still have accumulated partial scroll, |
702 | // unless we already are at one of the ends. |
703 | const float effective_offset = invertedControls ? -offset_accumulated : offset_accumulated; |
704 | if (effective_offset > 0.f && value < maximum) |
705 | return true; |
706 | if (effective_offset < 0.f && value > minimum) |
707 | return true; |
708 | offset_accumulated = 0; |
709 | return false; |
710 | } |
711 | } |
712 | |
713 | if (invertedControls) |
714 | stepsToScroll = -stepsToScroll; |
715 | |
716 | int prevValue = value; |
717 | position = bound(val: overflowSafeAdd(add: stepsToScroll)); // value will be updated by triggerAction() |
718 | q->triggerAction(action: QAbstractSlider::SliderMove); |
719 | |
720 | if (prevValue == value) { |
721 | offset_accumulated = 0; |
722 | return false; |
723 | } |
724 | return true; |
725 | } |
726 | |
727 | /*! |
728 | \reimp |
729 | */ |
730 | #if QT_CONFIG(wheelevent) |
731 | void QAbstractSlider::wheelEvent(QWheelEvent * e) |
732 | { |
733 | Q_D(QAbstractSlider); |
734 | e->ignore(); |
735 | bool vertical = bool(e->angleDelta().y()); |
736 | int delta = vertical ? e->angleDelta().y() : e->angleDelta().x(); |
737 | if (e->inverted()) |
738 | delta = -delta; |
739 | if (d->scrollByDelta(orientation: vertical ? Qt::Vertical : Qt::Horizontal, modifiers: e->modifiers(), delta)) |
740 | e->accept(); |
741 | } |
742 | |
743 | #endif |
744 | |
745 | /*! |
746 | \reimp |
747 | */ |
748 | void QAbstractSlider::keyPressEvent(QKeyEvent *ev) |
749 | { |
750 | Q_D(QAbstractSlider); |
751 | SliderAction action = SliderNoAction; |
752 | #ifdef QT_KEYPAD_NAVIGATION |
753 | if (ev->isAutoRepeat()) { |
754 | if (!d->firstRepeat.isValid()) |
755 | d->firstRepeat.start(); |
756 | else if (1 == d->repeatMultiplier) { |
757 | // This is the interval in milli seconds which one key repetition |
758 | // takes. |
759 | const int repeatMSecs = d->firstRepeat.elapsed(); |
760 | |
761 | /** |
762 | * The time it takes to currently navigate the whole slider. |
763 | */ |
764 | const qreal currentTimeElapse = (qreal(maximum()) / singleStep()) * repeatMSecs; |
765 | |
766 | /** |
767 | * This is an arbitrarily determined constant in msecs that |
768 | * specifies how long time it should take to navigate from the |
769 | * start to the end(excluding starting key auto repeat). |
770 | */ |
771 | const int SliderRepeatElapse = 2500; |
772 | |
773 | d->repeatMultiplier = currentTimeElapse / SliderRepeatElapse; |
774 | } |
775 | |
776 | } |
777 | else if (d->firstRepeat.isValid()) { |
778 | d->firstRepeat.invalidate(); |
779 | d->repeatMultiplier = 1; |
780 | } |
781 | |
782 | #endif |
783 | |
784 | switch (ev->key()) { |
785 | #ifdef QT_KEYPAD_NAVIGATION |
786 | case Qt::Key_Select: |
787 | if (QApplicationPrivate::keypadNavigationEnabled()) |
788 | setEditFocus(!hasEditFocus()); |
789 | else |
790 | ev->ignore(); |
791 | break; |
792 | case Qt::Key_Back: |
793 | if (QApplicationPrivate::keypadNavigationEnabled() && hasEditFocus()) { |
794 | setValue(d->origValue); |
795 | setEditFocus(false); |
796 | } else |
797 | ev->ignore(); |
798 | break; |
799 | #endif |
800 | |
801 | case Qt::Key_Left: |
802 | #ifdef QT_KEYPAD_NAVIGATION |
803 | // In QApplication::KeypadNavigationDirectional, we want to change the slider |
804 | // value if there is no left/right navigation possible and if this slider is not |
805 | // inside a tab widget. |
806 | if (QApplicationPrivate::keypadNavigationEnabled() |
807 | && (!hasEditFocus() && QApplication::navigationMode() == Qt::NavigationModeKeypadTabOrder |
808 | || d->orientation == Qt::Vertical |
809 | || !hasEditFocus() |
810 | && (QWidgetPrivate::canKeypadNavigate(Qt::Horizontal) || QWidgetPrivate::inTabWidget(this)))) { |
811 | ev->ignore(); |
812 | return; |
813 | } |
814 | if (QApplicationPrivate::keypadNavigationEnabled() && d->orientation == Qt::Vertical) |
815 | action = d->invertedControls ? SliderSingleStepSub : SliderSingleStepAdd; |
816 | else |
817 | #endif |
818 | if (isRightToLeft()) |
819 | action = d->invertedControls ? SliderSingleStepSub : SliderSingleStepAdd; |
820 | else |
821 | action = !d->invertedControls ? SliderSingleStepSub : SliderSingleStepAdd; |
822 | break; |
823 | case Qt::Key_Right: |
824 | #ifdef QT_KEYPAD_NAVIGATION |
825 | // Same logic as in Qt::Key_Left |
826 | if (QApplicationPrivate::keypadNavigationEnabled() |
827 | && (!hasEditFocus() && QApplication::navigationMode() == Qt::NavigationModeKeypadTabOrder |
828 | || d->orientation == Qt::Vertical |
829 | || !hasEditFocus() |
830 | && (QWidgetPrivate::canKeypadNavigate(Qt::Horizontal) || QWidgetPrivate::inTabWidget(this)))) { |
831 | ev->ignore(); |
832 | return; |
833 | } |
834 | if (QApplicationPrivate::keypadNavigationEnabled() && d->orientation == Qt::Vertical) |
835 | action = d->invertedControls ? SliderSingleStepAdd : SliderSingleStepSub; |
836 | else |
837 | #endif |
838 | if (isRightToLeft()) |
839 | action = d->invertedControls ? SliderSingleStepAdd : SliderSingleStepSub; |
840 | else |
841 | action = !d->invertedControls ? SliderSingleStepAdd : SliderSingleStepSub; |
842 | break; |
843 | case Qt::Key_Up: |
844 | #ifdef QT_KEYPAD_NAVIGATION |
845 | // In QApplication::KeypadNavigationDirectional, we want to change the slider |
846 | // value if there is no up/down navigation possible. |
847 | if (QApplicationPrivate::keypadNavigationEnabled() |
848 | && (QApplication::navigationMode() == Qt::NavigationModeKeypadTabOrder |
849 | || d->orientation == Qt::Horizontal |
850 | || !hasEditFocus() && QWidgetPrivate::canKeypadNavigate(Qt::Vertical))) { |
851 | ev->ignore(); |
852 | break; |
853 | } |
854 | #endif |
855 | action = d->invertedControls ? SliderSingleStepSub : SliderSingleStepAdd; |
856 | break; |
857 | case Qt::Key_Down: |
858 | #ifdef QT_KEYPAD_NAVIGATION |
859 | // Same logic as in Qt::Key_Up |
860 | if (QApplicationPrivate::keypadNavigationEnabled() |
861 | && (QApplication::navigationMode() == Qt::NavigationModeKeypadTabOrder |
862 | || d->orientation == Qt::Horizontal |
863 | || !hasEditFocus() && QWidgetPrivate::canKeypadNavigate(Qt::Vertical))) { |
864 | ev->ignore(); |
865 | break; |
866 | } |
867 | #endif |
868 | action = d->invertedControls ? SliderSingleStepAdd : SliderSingleStepSub; |
869 | break; |
870 | case Qt::Key_PageUp: |
871 | action = d->invertedControls ? SliderPageStepSub : SliderPageStepAdd; |
872 | break; |
873 | case Qt::Key_PageDown: |
874 | action = d->invertedControls ? SliderPageStepAdd : SliderPageStepSub; |
875 | break; |
876 | case Qt::Key_Home: |
877 | action = SliderToMinimum; |
878 | break; |
879 | case Qt::Key_End: |
880 | action = SliderToMaximum; |
881 | break; |
882 | default: |
883 | ev->ignore(); |
884 | break; |
885 | } |
886 | if (action) |
887 | triggerAction(action); |
888 | } |
889 | |
890 | /*! |
891 | \reimp |
892 | */ |
893 | void QAbstractSlider::changeEvent(QEvent *ev) |
894 | { |
895 | Q_D(QAbstractSlider); |
896 | switch (ev->type()) { |
897 | case QEvent::EnabledChange: |
898 | if (!isEnabled()) { |
899 | d->repeatActionTimer.stop(); |
900 | setSliderDown(false); |
901 | } |
902 | Q_FALLTHROUGH(); |
903 | default: |
904 | QWidget::changeEvent(ev); |
905 | } |
906 | } |
907 | |
908 | /*! |
909 | \reimp |
910 | */ |
911 | bool QAbstractSlider::event(QEvent *e) |
912 | { |
913 | #ifdef QT_KEYPAD_NAVIGATION |
914 | Q_D(QAbstractSlider); |
915 | switch (e->type()) { |
916 | case QEvent::FocusIn: |
917 | d->origValue = d->value; |
918 | break; |
919 | default: |
920 | break; |
921 | } |
922 | #endif |
923 | |
924 | return QWidget::event(event: e); |
925 | } |
926 | |
927 | // This function is called from itemviews when doing scroll per pixel (on updateGeometries()) |
928 | // It will not have any effect if there has been a call to setSingleStep with |
929 | // a 'reasonable' value (since viewMayChangeSingleStep will be set to false). |
930 | // (If setSingleStep is called with -1 it will however allow the views to change singleStep.) |
931 | |
932 | void QAbstractSliderPrivate::itemviewChangeSingleStep(int step) |
933 | { |
934 | singleStepFromItemView = step; |
935 | if (viewMayChangeSingleStep && singleStep != step) |
936 | setSteps(single: step, page: pageStep); |
937 | } |
938 | |
939 | QT_END_NAMESPACE |
940 | |
941 | #include "moc_qabstractslider.cpp" |
942 | |