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
qdial.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 "qdial.h"
6
7#include <qapplication.h>
8#include <qbitmap.h>
9#include <qcolor.h>
10#include <qevent.h>
11#include <qpainter.h>
12#include <qpolygon.h>
13#include <qregion.h>
14#include <qstyle.h>
15#include <qstylepainter.h>
16#include <qstyleoption.h>
17#include <qslider.h>
18#include <private/qabstractslider_p.h>
19#include <private/qmath_p.h>
20#if QT_CONFIG(accessibility)
21#include "qaccessible.h"
22#endif
23#include <qmath.h>
24
26
28{
29 Q_DECLARE_PUBLIC(QDial)
30public:
32 {
33 wrapping = false;
34 tracking = true;
35 doNotEmit = false;
36 target = qreal(3.7);
37 }
38
40 uint showNotches : 1;
41 uint wrapping : 1;
42 uint doNotEmit : 1;
43
44 int valueFromPoint(const QPoint &) const;
45 double angle(const QPoint &, const QPoint &) const;
46 void init();
47 virtual int bound(int val) const override;
48};
49
51{
52 Q_Q(QDial);
53 showNotches = false;
54 q->setFocusPolicy(Qt::WheelFocus);
55}
56
57int QDialPrivate::bound(int val) const
58{
59 if (wrapping) {
60 if ((val >= minimum) && (val <= maximum))
61 return val;
62 if (minimum == maximum)
63 return minimum;
64 val = minimum + ((val - minimum) % (maximum - minimum));
65 if (val < minimum)
66 val += maximum - minimum;
67 return val;
68 } else {
69 return QAbstractSliderPrivate::bound(val);
70 }
71}
72
73/*!
74 Initialize \a option with the values from this QDial. This method
75 is useful for subclasses when they need a QStyleOptionSlider, but don't want
76 to fill in all the information themselves.
77
78 \sa QStyleOption::initFrom()
79*/
80void QDial::initStyleOption(QStyleOptionSlider *option) const
81{
82 if (!option)
83 return;
84
85 Q_D(const QDial);
86 option->initFrom(this);
87 option->minimum = d->minimum;
88 option->maximum = d->maximum;
89 option->sliderPosition = d->position;
90 option->sliderValue = d->value;
91 option->singleStep = d->singleStep;
92 option->pageStep = d->pageStep;
93 option->upsideDown = !d->invertedAppearance;
94 option->notchTarget = d->target;
95 option->dialWrapping = d->wrapping;
96 option->subControls = QStyle::SC_All;
97 option->activeSubControls = QStyle::SC_None;
98 if (!d->showNotches) {
99 option->subControls &= ~QStyle::SC_DialTickmarks;
100 option->tickPosition = QSlider::TicksAbove;
101 } else {
102 option->tickPosition = QSlider::NoTicks;
103 }
104 option->tickInterval = notchSize();
105}
106
107int QDialPrivate::valueFromPoint(const QPoint &p) const
108{
109 Q_Q(const QDial);
110 double yy = q->height()/2.0 - p.y();
111 double xx = p.x() - q->width()/2.0;
112 double a = (xx || yy) ? std::atan2(yy, xx) : 0;
113
114 if (a < Q_PI / -2)
115 a = a + Q_PI * 2;
116
117 int dist = 0;
118 int minv = minimum, maxv = maximum;
119
120 if (minimum < 0) {
121 dist = -minimum;
122 minv = 0;
123 maxv = maximum + dist;
124 }
125
126 int r = maxv - minv;
127 int v;
128 if (wrapping)
129 v = (int)(0.5 + minv + r * (Q_PI * 3 / 2 - a) / (2 * Q_PI));
130 else
131 v = (int)(0.5 + minv + r* (Q_PI * 4 / 3 - a) / (Q_PI * 10 / 6));
132
133 if (dist > 0)
134 v -= dist;
135
136 return !invertedAppearance ? bound(v) : maximum - bound(v);
137}
138
139/*!
140 \class QDial
141
142 \brief The QDial class provides a rounded range control (like a speedometer or potentiometer).
143
144 \ingroup basicwidgets
145 \inmodule QtWidgets
146
147 \image fusion-dial.png {Round dial with notches}
148
149 QDial is used when the user needs to control a value within a
150 program-definable range, and the range either wraps around
151 (for example, with angles measured from 0 to 359 degrees) or the
152 dialog layout needs a square widget.
153
154 Since QDial inherits from QAbstractSlider, the dial behaves in
155 a similar way to a \l{QSlider}{slider}. When wrapping() is false
156 (the default setting) there is no real difference between a slider
157 and a dial. They both share the same signals, slots and member
158 functions. Which one you use depends on the expectations of
159 your users and on the type of application.
160
161 The dial initially emits valueChanged() signals continuously while
162 the slider is being moved; you can make it emit the signal less
163 often by disabling the \l{QAbstractSlider::tracking} {tracking}
164 property. The sliderMoved() signal is emitted continuously even
165 when tracking is disabled.
166
167 The dial also emits sliderPressed() and sliderReleased() signals
168 when the mouse button is pressed and released. Note that the
169 dial's value can change without these signals being emitted since
170 the keyboard and wheel can also be used to change the value.
171
172 Unlike the slider, QDial attempts to draw a "nice" number of
173 notches rather than one per line step. If possible, the number of
174 notches drawn is one per line step, but if there aren't enough pixels
175 to draw every one, QDial will skip notches to try and draw a uniform
176 set (e.g. by drawing every second or third notch).
177
178 Like the slider, the dial makes the QAbstractSlider function setValue()
179 available as a slot.
180
181 The dial's keyboard interface is fairly simple: The
182 \uicontrol{left}/\uicontrol{up} and \uicontrol{right}/\uicontrol{down} arrow keys adjust
183 the dial's \l {QAbstractSlider::value} {value} by the defined
184 \l {QAbstractSlider::singleStep} {singleStep}, \uicontrol{Page Up} and
185 \uicontrol{Page Down} by the defined \l {QAbstractSlider::pageStep}
186 {pageStep}, and the \uicontrol Home and \uicontrol End keys set the value to
187 the defined \l {QAbstractSlider::minimum} {minimum} and
188 \l {QAbstractSlider::maximum} {maximum} values.
189
190 If you are using the mouse wheel to adjust the dial, the increment
191 value is determined by the lesser value of
192 \l{QApplication::wheelScrollLines()} {wheelScrollLines} multiplied
193 by \l {QAbstractSlider::singleStep} {singleStep}, and
194 \l {QAbstractSlider::pageStep} {pageStep}.
195
196 \sa QScrollBar, QSpinBox, QSlider, {Sliders Example}
197*/
198
199/*!
200 Constructs a dial.
201
202 The \a parent argument is sent to the QAbstractSlider constructor.
203*/
204QDial::QDial(QWidget *parent)
205 : QAbstractSlider(*new QDialPrivate, parent)
206{
207 Q_D(QDial);
208 d->init();
209}
210
211/*!
212 Destroys the dial.
213*/
214QDial::~QDial()
215{
216}
217
218/*! \reimp */
219void QDial::resizeEvent(QResizeEvent *e)
220{
221 QWidget::resizeEvent(e);
222}
223
224/*!
225 \reimp
226*/
227
228void QDial::paintEvent(QPaintEvent *)
229{
230 QStylePainter p(this);
231 QStyleOptionSlider option;
232 initStyleOption(&option);
233 p.drawComplexControl(QStyle::CC_Dial, option);
234}
235
236/*!
237 \reimp
238*/
239
240void QDial::mousePressEvent(QMouseEvent *e)
241{
242 Q_D(QDial);
243 if (d->maximum == d->minimum ||
244 (e->button() != Qt::LeftButton) ||
245 (e->buttons() ^ e->button())) {
246 e->ignore();
247 return;
248 }
249 e->accept();
250 setSliderPosition(d->valueFromPoint(e->position().toPoint()));
251 // ### This isn't quite right,
252 // we should be doing a hit test and only setting this if it's
253 // the actual dial thingie (similar to what QSlider does), but we have no
254 // subControls for QDial.
255 setSliderDown(true);
256}
257
258
259/*!
260 \reimp
261*/
262
263void QDial::mouseReleaseEvent(QMouseEvent * e)
264{
265 Q_D(QDial);
266 if (e->buttons() & (~e->button()) ||
267 (e->button() != Qt::LeftButton)) {
268 e->ignore();
269 return;
270 }
271 e->accept();
272 setValue(d->valueFromPoint(e->position().toPoint()));
273 setSliderDown(false);
274}
275
276
277/*!
278 \reimp
279*/
280
281void QDial::mouseMoveEvent(QMouseEvent * e)
282{
283 Q_D(QDial);
284 if (!(e->buttons() & Qt::LeftButton)) {
285 e->ignore();
286 return;
287 }
288 e->accept();
289 d->doNotEmit = true;
290 setSliderPosition(d->valueFromPoint(e->position().toPoint()));
291 d->doNotEmit = false;
292}
293
294
295/*!
296 \reimp
297*/
298
299void QDial::sliderChange(SliderChange change)
300{
301 QAbstractSlider::sliderChange(change);
302}
303
304void QDial::setWrapping(bool enable)
305{
306 Q_D(QDial);
307 if (d->wrapping == enable)
308 return;
309 d->wrapping = enable;
310 update();
311}
312
313
314/*!
315 \property QDial::wrapping
316 \brief whether wrapping is enabled
317
318 If true, wrapping is enabled; otherwise some space is inserted at the bottom
319 of the dial to separate the ends of the range of valid values.
320
321 If enabled, the arrow can be oriented at any angle on the dial. If disabled,
322 the arrow will be restricted to the upper part of the dial; if it is rotated
323 into the space at the bottom of the dial, it will be clamped to the closest
324 end of the valid range of values.
325
326 By default this property is \c false.
327*/
328
329bool QDial::wrapping() const
330{
331 Q_D(const QDial);
332 return d->wrapping;
333}
334
335
336/*!
337 \property QDial::notchSize
338 \brief the current notch size
339
340 The notch size is in range control units, not pixels, and is
341 calculated to be a multiple of singleStep() that results in an
342 on-screen notch size near notchTarget().
343
344 \sa notchTarget(), singleStep()
345*/
346
347int QDial::notchSize() const
348{
349 Q_D(const QDial);
350 // radius of the arc
351 qreal r = qMin(width(), height())/2.0;
352 // length of the whole arc
353 int l = qRound(r * (d->wrapping ? 6.0 : 5.0) * Q_PI / 6.0);
354 // length of the arc from minValue() to minValue()+pageStep()
355 if (d->maximum > d->minimum + d->pageStep)
356 l = qRound(l * d->pageStep / double(d->maximum - d->minimum));
357 // length of a singleStep arc
358 l = qMax(l * d->singleStep / (d->pageStep ? d->pageStep : 1), 1);
359 // how many times singleStep can be draw in d->target pixels
360 l = qMax(qRound(d->target / l), 1);
361 // we want notchSize() to be a non-zero multiple of singleStep()
362 return d->singleStep * l;
363}
364
365void QDial::setNotchTarget(double target)
366{
367 Q_D(QDial);
368 d->target = target;
369 update();
370}
371
372/*!
373 \property QDial::notchTarget
374 \brief the target number of pixels between notches
375
376 The notch target is the number of pixels QDial attempts to put
377 between each notch.
378
379 The actual size may differ from the target size.
380
381 The default notch target is 3.7 pixels.
382*/
383qreal QDial::notchTarget() const
384{
385 Q_D(const QDial);
386 return d->target;
387}
388
389
390void QDial::setNotchesVisible(bool visible)
391{
392 Q_D(QDial);
393 d->showNotches = visible;
394 update();
395}
396
397/*!
398 \property QDial::notchesVisible
399 \brief whether the notches are shown
400
401 If the property is \c true, a series of notches are drawn around the dial
402 to indicate the range of values available; otherwise no notches are
403 shown.
404
405 By default, this property is disabled.
406*/
407bool QDial::notchesVisible() const
408{
409 Q_D(const QDial);
410 return d->showNotches;
411}
412
413/*!
414 \reimp
415*/
416
417QSize QDial::minimumSizeHint() const
418{
419 return QSize(50, 50);
420}
421
422/*!
423 \reimp
424*/
425
426QSize QDial::sizeHint() const
427{
428 return QSize(100, 100);
429}
430
431/*!
432 \reimp
433*/
434bool QDial::event(QEvent *e)
435{
436 return QAbstractSlider::event(e);
437}
438
439QT_END_NAMESPACE
440
441#include "moc_qdial.cpp"
uint wrapping
Definition qdial.cpp:41
uint doNotEmit
Definition qdial.cpp:42
virtual int bound(int val) const override
Definition qdial.cpp:57
void init()
Definition qdial.cpp:50
uint showNotches
Definition qdial.cpp:40
double angle(const QPoint &, const QPoint &) const
int valueFromPoint(const QPoint &) const
Definition qdial.cpp:107