CVB++ 15.0
detail_spinbox_slider.hpp
1#pragma once
2
3#pragma warning(push)
4#pragma warning(disable : 4800)
5#pragma warning(disable : 4251)
6#pragma warning(disable : 4244)
7#include <QFrame>
8#include <QPushButton>
9#include <QHBoxLayout>
10#include <QDialog>
11#include <QEvent>
12#pragma warning(pop)
13
14#include "../../global.hpp"
15#include "detail_spinbox64.hpp"
16#include "detail_label_slider.hpp"
17
18namespace Cvb
19{
20
21 CVB_BEGIN_INLINE_NS
22
23 namespace UI
24 {
25 namespace Private
26 {
27
28 class SpinBoxSlider64 : public QFrame
29 {
30
31 Q_OBJECT
32
33 public:
34 explicit SpinBoxSlider64(QWidget *parent = 0)
35 : QFrame(parent)
36 , sliderDialog_(nullptr)
37 , sliderCoarse_(nullptr)
38 , sliderFine_(nullptr)
39 {
40 auto layout = new QHBoxLayout(this); // NOLINT(cppcoreguidelines-owning-memory)
41 layout->setContentsMargins(0, 0, 0, 0);
42 layout->setSpacing(0);
43
44 spinBox_ = new SpinBox64(); // NOLINT
45 layout->addWidget(spinBox_);
46 QObject::connect(spinBox_, &SpinBox64::ValueChanged, this, &SpinBoxSlider64::SpinBoxChanged,
47 Qt::QueuedConnection);
48
49 button_ = new QPushButton("..."); // NOLINT
50 button_->setFixedWidth(20);
51 layout->setStretch(0, 1);
52 layout->addWidget(button_);
53 QObject::connect(button_, &QPushButton::pressed, this, &SpinBoxSlider64::ButtonPressed, Qt::QueuedConnection);
54
55 installEventFilter(this);
56
57 if (!CanCast())
58 {
59 button_->hide();
60 }
61 }
62
63 void SetMinimum(qint64 minimum)
64 {
65 spinBox_->setMinimum(minimum);
66
67 if (sliderFine_)
68 sliderFine_->SetMinimum((int)minimum);
69
70 if (CanCast())
71 button_->show();
72 else
73 button_->hide();
74 }
75
76 void SetMaximum(qint64 maximum)
77 {
78 spinBox_->setMaximum(maximum);
79
80 if (CanCast())
81 button_->show();
82 else
83 button_->hide();
84 }
85
86 void SetRange(qint64 minimum, qint64 maximum)
87 {
88 spinBox_->setRange(minimum, maximum);
89
90 if (CanCast())
91 button_->show();
92 else
93 button_->hide();
94 }
95
96 qint64 Minimum() const
97 {
98 return spinBox_->minimum();
99 }
100
101 qint64 Maximum() const
102 {
103 return spinBox_->maximum();
104 }
105
106 void SetValue(qint64 val)
107 {
108 spinBox_->setValue(val);
109 }
110
111 qint64 Value() const
112 {
113 return spinBox_->value();
114 }
115
116 void SetSingleStep(qint64 val)
117 {
118 spinBox_->setSingleStep(val);
119 }
120
121 qint64 SingleStep() const
122 {
123 return spinBox_->singleStep();
124 }
125
126 Q_SIGNALS:
127
128 void ValueChanged(qint64 value);
129
130 public Q_SLOTS:
131
132 void SliderFineChanged(int value)
133 {
134 // Adjust spinbox
135 auto oldState = spinBox_->blockSignals(true);
136 spinBox_->setValue(value);
137 spinBox_->blockSignals(oldState);
138
139 // Adjust the coarse slider
140 oldState = sliderCoarse_->blockSignals(true);
141 sliderCoarse_->SetValue(value);
142 sliderCoarse_->blockSignals(oldState);
143
144 ValueChanged(static_cast<qint64>(value)); // send signal
145 }
146
147 void SliderCoarseChanged(int value)
148 {
149 // Adjust spinbox
150 auto oldState = spinBox_->blockSignals(true);
151 spinBox_->setValue(value);
152 spinBox_->blockSignals(oldState);
153
154 // Adjust the fine slider
155 oldState = sliderFine_->blockSignals(true);
156 SetFineSliderRanges(value);
157 SetFineSlider(value);
158 sliderFine_->blockSignals(oldState);
159
160 ValueChanged(static_cast<qint64>(value)); // send signal
161 }
162
163 void SpinBoxChanged(qint64 value)
164 {
165 // Adjust fine slider
166 if (sliderFine_)
167 {
168 auto oldState = sliderFine_->blockSignals(true);
169 SetFineSliderRanges(static_cast<int>(value)); // only called if CanCast() is true
170 SetFineSlider(static_cast<int>(value)); // only called if CanCast() is true
171 sliderFine_->blockSignals(oldState);
172 }
173
174 // Adjust coarse slider
175 if (sliderCoarse_)
176 {
177 auto oldState = sliderCoarse_->blockSignals(true);
178 sliderCoarse_->SetValue(static_cast<int>(value)); // only called if CanCast() is true
179 sliderCoarse_->blockSignals(oldState);
180 }
181
182 ValueChanged(value); // send signal
183 }
184
185 void ButtonPressed()
186 {
187 if (CanCast())
188 {
189 auto min = static_cast<int>(Minimum());
190 auto max = static_cast<int>(Maximum());
191 auto value = static_cast<int>(Value());
192 ShowSliderDialog(min, max, value);
193 }
194 }
195
196 void HideDialogBox()
197 {
198 if (sliderDialog_)
199 sliderDialog_->hide();
200 }
201
202 void ShowDialogBox()
203 {
204 if (sliderDialog_)
205 sliderDialog_->show();
206 }
207
208 bool IsDialogBoxVisible()
209 {
210 if (sliderDialog_)
211 return sliderDialog_->isVisible();
212 return false;
213 }
214
215 protected:
216 bool eventFilter(QObject *obj, QEvent *event) override
217 {
218 if (sliderDialog_)
219 {
220 button_->updateGeometry();
221 auto g = button_->geometry();
222
223 auto buttonPos = button_->rect().bottomRight();
224 auto buttonPosGlob = button_->mapToGlobal(buttonPos);
225
226 auto x = buttonPosGlob.x();
227 auto y = buttonPosGlob.y();
228
229 // allow a range of change of 60 (otherwise the dialog can flicker)
230 static const int range = 60;
231 if (buttonPosGlobal_.x() < x - range || buttonPosGlobal_.x() > x + range || buttonPosGlobal_.y() < y - range
232 || buttonPosGlobal_.y() > y + range)
233 {
234 sliderDialog_->move(buttonPosGlob.x() - sliderDialog_->width(), buttonPosGlob.y());
235 buttonPosGlobal_ = buttonPosGlob;
236 }
237 }
238
239 return QObject::eventFilter(obj, event);
240 }
241
242 private:
243 void ShowSliderDialog(int min, int max, int value)
244 {
245 if (sliderDialog_)
246 {
247 if (sliderDialog_->isVisible())
248 sliderDialog_->hide();
249 else
250 sliderDialog_->show();
251 return;
252 }
253
254 sliderDialog_ = new QDialog(this); // NOLINT(cppcoreguidelines-owning-memory)
255 sliderDialog_->setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint);
256 sliderDialog_->setObjectName("sliderDialog");
257 sliderDialog_->setStyleSheet("#sliderDialog{border:1px solid black}");
258
259 auto layout = new QHBoxLayout(sliderDialog_); // NOLINT(cppcoreguidelines-owning-memory)
260 layout->setContentsMargins(0, 0, 0, 0);
261 layout->setSpacing(0);
262
263 sliderFine_ = new LabelSlider(Qt::Vertical, false, sliderDialog_); // NOLINT(cppcoreguidelines-owning-memory)
264 sliderFine_->SetMinimum(FineSliderMin(value));
265 sliderFine_->SetMaximum(FineSliderMax(value));
266 sliderFine_->SetSingleStep(static_cast<int>(spinBox_->singleStep())); // only called if CanCast() is true
267 sliderFine_->SetValue(value);
268
269 QObject::connect(sliderFine_, &LabelSlider::ValueChanged, this, &SpinBoxSlider64::SliderFineChanged,
270 Qt::QueuedConnection);
271
272 sliderCoarse_ =
273 new LabelSlider(Qt::Vertical, true, sliderDialog_); // NOLINT(cppcoreguidelines-owning-memory)
274 sliderCoarse_->SetMinimum(min);
275 sliderCoarse_->SetMaximum(max);
276 sliderCoarse_->SetSingleStep(FineSliderSubRange());
277 sliderCoarse_->SetBoldLabel(true);
278 sliderCoarse_->SetValue(value);
279
280 QString scrollMin = "<b>" + QString::number(min) + "</b>";
281 QString scrollMax = "<b>" + QString::number(max) + "</b>";
282
283 layout->addWidget(sliderFine_);
284
285 // Only add the coarse slider, if necessary
286 if ((sliderCoarse_->Maximum() - sliderCoarse_->Minimum() > sliderCoarse_->SingleStep()))
287 layout->addWidget(sliderCoarse_);
288
289 QObject::connect(sliderCoarse_, &LabelSlider::ValueChanged, this, &SpinBoxSlider64::SliderCoarseChanged,
290 Qt::QueuedConnection);
291
292 sliderDialog_->adjustSize();
293
294 button_->updateGeometry();
295 auto buttonPos = button_->rect().bottomRight();
296 auto buttonPosGlob = button_->mapToGlobal(buttonPos);
297 sliderDialog_->move(buttonPosGlob.x() - sliderDialog_->width(), buttonPosGlob.y());
298 buttonPosGlobal_ = buttonPosGlob;
299
300 sliderDialog_->show();
301 }
302
303 int FineSliderMin(int value)
304 {
305 auto minimum = spinBox_->minimum();
306
307 if (value > spinBox_->maximum())
308 value = static_cast<int>(spinBox_->maximum()); // only called if CanCast() is true
309
310 auto min = value - FineSliderSubRange();
311 if (min <= minimum)
312 min = static_cast<int>(minimum); // only called if CanCast() is true
313
314 return min;
315 }
316
317 int FineSliderMax(int value)
318 {
319 auto maximum = spinBox_->maximum();
320
321 if (value < spinBox_->minimum())
322 value = static_cast<int>(spinBox_->minimum()); // only called if CanCast() is true
323
324 auto max = value + FineSliderSubRange();
325 if (max >= maximum)
326 max = static_cast<int>(maximum); // only called if CanCast() is true
327
328 return max;
329 }
330
331 void SetFineSliderRanges(int value)
332 {
333 if (value >= sliderFine_->Minimum() && value <= sliderFine_->Maximum())
334 return;
335
336 sliderFine_->SetMinimum(FineSliderMin(value));
337 sliderFine_->SetMaximum(FineSliderMax(value));
338 }
339
340 void SetFineSlider(int value)
341 {
342 if (value >= sliderFine_->Minimum() && value <= sliderFine_->Maximum())
343 {
344 sliderFine_->SetValue(value);
345 }
346 else
347 {
348 SetFineSliderRanges(value);
349 sliderFine_->SetValue(value);
350 }
351 }
352
353 bool IsInRange(int value)
354 {
355 if (value >= spinBox_->minimum() && value <= spinBox_->maximum())
356 return true;
357 return false;
358 }
359
360 int FineSliderSubRange()
361 {
362 auto subrange = MAX_FINE_SLIDER_RANGE;
363 while (subrange % SingleStep() != 0)
364 --subrange;
365 return subrange;
366 }
367
368 bool CanCast()
369 {
370 return !(Minimum() < std::numeric_limits<int>::min() || Minimum() > std::numeric_limits<int>::max()
371 || Maximum() < std::numeric_limits<int>::min() || Maximum() > std::numeric_limits<int>::max());
372 }
373
374 private:
375 SpinBox64 *spinBox_;
376 QPushButton *button_;
377 QPoint buttonPosGlobal_;
378 QDialog *sliderDialog_;
379 LabelSlider *sliderCoarse_;
380 LabelSlider *sliderFine_;
381
382 static const int MAX_FINE_SLIDER_RANGE = 250;
383
384 }; /* class SpinBoxSlider64 */
385
386 } /* namespace Private */
387 } /* namespace UI */
388
389 CVB_END_INLINE_NS
390
391} /* namespace Cvb */
T max(T... args)
T min(T... args)
Namespace for user interface components.
Definition decl_image_scene.hpp:39
Root namespace for the Image Manager interface.
Definition c_bayer_to_rgb.h:17