VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/extensions/QIAdvancedSlider.cpp

Last change on this file was 106061, checked in by vboxsync, 3 weeks ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.8 KB
Line 
1/* $Id: QIAdvancedSlider.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - Qt extensions: QIAdvancedSlider class implementation.
4 */
5
6/*
7 * Copyright (C) 2009-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/* Qt includes: */
29#include <QPainter>
30#include <QSlider>
31#include <QStyle>
32#include <QVBoxLayout>
33
34/* GUI includes: */
35#include "QIAdvancedSlider.h"
36
37/* Qt includes: */
38#include <QStyleOptionSlider>
39
40/* External includes: */
41#include <math.h>
42
43
44/** QSlider subclass for our private needs. */
45class UIPrivateSlider : public QSlider
46{
47 Q_OBJECT;
48
49public:
50
51 /** Constructs private-slider passing @a pParent and @a enmOrientation to the base-class. */
52 UIPrivateSlider(Qt::Orientation enmOrientation, QWidget *pParent = 0);
53
54 /** Returns suitable position for @a iValue. */
55 int positionForValue(int iValue) const;
56
57 /** @todo encapsulate below stuff accordingly.. */
58
59 /** Holds the minimum optimal border. */
60 int m_minOpt;
61 /** Holds the maximum optimal border. */
62 int m_maxOpt;
63 /** Holds the minimum warning border. */
64 int m_minWrn;
65 /** Holds the maximum warning border. */
66 int m_maxWrn;
67 /** Holds the minimum error border. */
68 int m_minErr;
69 /** Holds the maximum error border. */
70 int m_maxErr;
71
72protected:
73
74 /** Handles paint @a pEvent. */
75 virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE RT_FINAL;
76
77private:
78
79 /** Holds the optimal color. */
80 QColor m_optColor;
81 /** Holds the warning color. */
82 QColor m_wrnColor;
83 /** Holds the error color. */
84 QColor m_errColor;
85};
86
87
88/*********************************************************************************************************************************
89* Class UIPrivateSlider implementation. *
90*********************************************************************************************************************************/
91
92UIPrivateSlider::UIPrivateSlider(Qt::Orientation enmOrientation, QWidget *pParent /* = 0 */)
93 : QSlider(enmOrientation, pParent)
94 , m_minOpt(-1)
95 , m_maxOpt(-1)
96 , m_minWrn(-1)
97 , m_maxWrn(-1)
98 , m_minErr(-1)
99 , m_maxErr(-1)
100 , m_optColor(0x0, 0xff, 0x0, 0x3c)
101 , m_wrnColor(0xff, 0x54, 0x0, 0x3c)
102 , m_errColor(0xff, 0x0, 0x0, 0x3c)
103{
104 /* Make sure ticks *always* positioned below: */
105 setTickPosition(QSlider::TicksBelow);
106}
107
108int UIPrivateSlider::positionForValue(int iValue) const
109{
110 QStyleOptionSlider opt;
111 initStyleOption(&opt);
112 opt.subControls = QStyle::SC_All;
113 int iAvailable = opt.rect.width() - style()->pixelMetric(QStyle::PM_SliderLength, &opt, this);
114 return QStyle::sliderPositionFromValue(opt.minimum, opt.maximum, iValue, iAvailable);
115}
116
117void UIPrivateSlider::paintEvent(QPaintEvent *pEvent)
118{
119 QPainter p(this);
120
121 QStyleOptionSlider opt;
122 initStyleOption(&opt);
123 opt.subControls = QStyle::SC_All;
124
125 int iAvailable = opt.rect.width() - style()->pixelMetric(QStyle::PM_SliderLength, &opt, this);
126 QSize s = size();
127
128 /* We want to acquire SC_SliderTickmarks sub-control rectangle
129 * and fill it with necessary background colors: */
130#ifdef VBOX_WS_MAC
131 // WORKAROUND:
132 // Under MacOS X SC_SliderTickmarks is not fully reliable
133 // source of the information we need, providing us with incorrect width.
134 // So we have to calculate tickmarks rectangle ourself.
135 QRect ticks = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderTickmarks, this);
136 ticks.setRect((s.width() - iAvailable) / 2, s.height() - ticks.y(), iAvailable, ticks.height());
137#else /* VBOX_WS_MAC */
138 // WORKAROUND:
139 // Under Windows SC_SliderTickmarks is fully unreliable
140 // source of the information we need, providing us with empty rectangle.
141 // Under X11 SC_SliderTickmarks is not fully reliable
142 // source of the information we need, providing us with different rectangles
143 // (correct or incorrect) under different look&feel styles.
144 // So we have to calculate tickmarks rectangle ourself.
145 QRect ticks = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this) |
146 style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, this);
147 ticks.setRect((s.width() - iAvailable) / 2, ticks.bottom() + 1, iAvailable, s.height() - ticks.bottom() - 1);
148#endif /* VBOX_WS_MAC */
149
150 if ((m_minOpt != -1 &&
151 m_maxOpt != -1) &&
152 m_minOpt != m_maxOpt)
153 {
154 int iPosMinOpt = QStyle::sliderPositionFromValue(opt.minimum, opt.maximum, m_minOpt, iAvailable);
155 int iPosMaxOpt = QStyle::sliderPositionFromValue(opt.minimum, opt.maximum, m_maxOpt, iAvailable);
156 p.fillRect(ticks.x() + iPosMinOpt, ticks.y(), iPosMaxOpt - iPosMinOpt + 1, ticks.height(), m_optColor);
157 }
158 if ((m_minWrn != -1 &&
159 m_maxWrn != -1) &&
160 m_minWrn != m_maxWrn)
161 {
162 int iPosMinWrn = QStyle::sliderPositionFromValue(opt.minimum, opt.maximum, m_minWrn, iAvailable);
163 int iPosMaxWrn = QStyle::sliderPositionFromValue(opt.minimum, opt.maximum, m_maxWrn, iAvailable);
164 p.fillRect(ticks.x() + iPosMinWrn, ticks.y(), iPosMaxWrn - iPosMinWrn + 1, ticks.height(), m_wrnColor);
165 }
166 if ((m_minErr != -1 &&
167 m_maxErr != -1) &&
168 m_minErr != m_maxErr)
169 {
170 int iPosMinErr = QStyle::sliderPositionFromValue(opt.minimum, opt.maximum, m_minErr, iAvailable);
171 int iPosMaxErr = QStyle::sliderPositionFromValue(opt.minimum, opt.maximum, m_maxErr, iAvailable);
172 p.fillRect(ticks.x() + iPosMinErr, ticks.y(), iPosMaxErr - iPosMinErr + 1, ticks.height(), m_errColor);
173 }
174 p.end();
175
176 /* Call to base-class: */
177 QSlider::paintEvent(pEvent);
178}
179
180
181/*********************************************************************************************************************************
182* Class QIAdvancedSlider implementation. *
183*********************************************************************************************************************************/
184
185QIAdvancedSlider::QIAdvancedSlider(QWidget *pParent /* = 0 */)
186 : QWidget(pParent)
187{
188 prepare();
189}
190
191QIAdvancedSlider::QIAdvancedSlider(Qt::Orientation enmOrientation, QWidget *pParent /* = 0 */)
192 : QWidget(pParent)
193{
194 prepare(enmOrientation);
195}
196
197int QIAdvancedSlider::value() const
198{
199 return m_pSlider->value();
200}
201
202void QIAdvancedSlider::setRange(int iMin, int iMax)
203{
204 m_pSlider->setRange(iMin, iMax);
205}
206
207void QIAdvancedSlider::setMaximum(int iValue)
208{
209 m_pSlider->setMaximum(iValue);
210}
211
212int QIAdvancedSlider::maximum() const
213{
214 return m_pSlider->maximum();
215}
216
217void QIAdvancedSlider::setMinimum(int iValue)
218{
219 m_pSlider->setMinimum(iValue);
220}
221
222int QIAdvancedSlider::minimum() const
223{
224 return m_pSlider->minimum();
225}
226
227void QIAdvancedSlider::setPageStep(int iValue)
228{
229 m_pSlider->setPageStep(iValue);
230}
231
232int QIAdvancedSlider::pageStep() const
233{
234 return m_pSlider->pageStep();
235}
236
237void QIAdvancedSlider::setSingleStep(int iValue)
238{
239 m_pSlider->setSingleStep(iValue);
240}
241
242int QIAdvancedSlider::singelStep() const
243{
244 return m_pSlider->singleStep();
245}
246
247void QIAdvancedSlider::setTickInterval(int iValue)
248{
249 m_pSlider->setTickInterval(iValue);
250}
251
252int QIAdvancedSlider::tickInterval() const
253{
254 return m_pSlider->tickInterval();
255}
256
257Qt::Orientation QIAdvancedSlider::orientation() const
258{
259 return m_pSlider->orientation();
260}
261
262void QIAdvancedSlider::setSnappingEnabled(bool fOn)
263{
264 m_fSnappingEnabled = fOn;
265}
266
267bool QIAdvancedSlider::isSnappingEnabled() const
268{
269 return m_fSnappingEnabled;
270}
271
272void QIAdvancedSlider::setOptimalHint(int iMin, int iMax)
273{
274 m_pSlider->m_minOpt = iMin;
275 m_pSlider->m_maxOpt = iMax;
276
277 update();
278}
279
280void QIAdvancedSlider::setWarningHint(int iMin, int iMax)
281{
282 m_pSlider->m_minWrn = iMin;
283 m_pSlider->m_maxWrn = iMax;
284
285 update();
286}
287
288void QIAdvancedSlider::setErrorHint(int iMin, int iMax)
289{
290 m_pSlider->m_minErr = iMin;
291 m_pSlider->m_maxErr = iMax;
292
293 update();
294}
295
296void QIAdvancedSlider::setToolTip(const QString &strToolTip)
297{
298 m_pSlider->setToolTip(strToolTip);
299}
300
301void QIAdvancedSlider::setOrientation(Qt::Orientation enmOrientation)
302{
303 m_pSlider->setOrientation(enmOrientation);
304}
305
306void QIAdvancedSlider::setValue (int iValue)
307{
308 m_pSlider->setValue(iValue);
309}
310
311void QIAdvancedSlider::sltSliderMoved(int iValue)
312{
313 iValue = snapValue(iValue);
314 m_pSlider->setValue(iValue);
315 emit sliderMoved(iValue);
316}
317
318void QIAdvancedSlider::prepare(Qt::Orientation enmOrientation /* = Qt::Horizontal */)
319{
320 m_fSnappingEnabled = false;
321
322 /* Create layout: */
323 QVBoxLayout *pMainLayout = new QVBoxLayout(this);
324 if (pMainLayout)
325 {
326 /* Configure layout: */
327 pMainLayout->setContentsMargins(0, 0, 0, 0);
328
329 /* Create private-slider: */
330 m_pSlider = new UIPrivateSlider(enmOrientation, this);
331 if (m_pSlider)
332 {
333 connect(m_pSlider, &UIPrivateSlider::sliderMoved, this, &QIAdvancedSlider::sltSliderMoved);
334 connect(m_pSlider, &UIPrivateSlider::valueChanged, this, &QIAdvancedSlider::valueChanged);
335 connect(m_pSlider, &UIPrivateSlider::sliderPressed, this, &QIAdvancedSlider::sliderPressed);
336 connect(m_pSlider, &UIPrivateSlider::sliderReleased, this, &QIAdvancedSlider::sliderReleased);
337
338 /* Add into layout: */
339 pMainLayout->addWidget(m_pSlider);
340 }
341 }
342}
343
344int QIAdvancedSlider::snapValue(int iValue)
345{
346 if (m_fSnappingEnabled &&
347 iValue > 2)
348 {
349 float l2 = log((float)iValue)/log(2.0);
350 int iNewVal = (int)pow((float)2, (int)qRound(l2)); /* The value to snap on */
351 int iPos = m_pSlider->positionForValue(iValue); /* Get the relative screen pos for the original value */
352 int iNewPos = m_pSlider->positionForValue(iNewVal); /* Get the relative screen pos for the snap value */
353 if (abs(iNewPos - iPos) < 5) /* 10 pixel snapping range */
354 {
355 iValue = iNewVal;
356 if (iValue > m_pSlider->maximum())
357 iValue = m_pSlider->maximum();
358 else if (iValue < m_pSlider->minimum())
359 iValue = m_pSlider->minimum();
360 }
361 }
362 return iValue;
363}
364
365
366#include "QIAdvancedSlider.moc"
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette