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