• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mcallegari / qlcplus / 9306117670

30 May 2024 04:38PM UTC coverage: 32.028% (+0.02%) from 32.013%
9306117670

push

github

mcallegari
Enter 4.13.1 release

14020 of 43774 relevant lines covered (32.03%)

23948.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

45.07
/ui/src/ctkrangeslider.cpp
1
/*=========================================================================
2

3
  Library:   CTK
4

5
  Copyright (c) Kitware Inc.
6

7
  Licensed under the Apache License, Version 2.0 (the "License");
8
  you may not use this file except in compliance with the License.
9
  You may obtain a copy of the License at
10

11
      http://www.apache.org/licenses/LICENSE-2.0.txt
12

13
  Unless required by applicable law or agreed to in writing, software
14
  distributed under the License is distributed on an "AS IS" BASIS,
15
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
  See the License for the specific language governing permissions and
17
  limitations under the License.
18

19
=========================================================================*/
20

21
// Qt includes
22
#include <QDebug>
23
#include <QMouseEvent>
24
#include <QKeyEvent>
25
#include <QStyleOptionSlider>
26
#include <QApplication>
27
#include <QStylePainter>
28
#include <QStyle>
29
#include <QToolTip>
30

31
// CTK includes
32
#include "ctkrangeslider.h"
33

34
class ctkRangeSliderPrivate
18✔
35
{
36
    Q_DECLARE_PUBLIC(ctkRangeSlider);
37
protected:
38
    ctkRangeSlider *const q_ptr;
39
public:
40
    /// Boolean indicates the selected handle
41
    ///   True for the minimum range handle, false for the maximum range handle
42
    enum Handle {
43
        NoHandle = 0x0,
44
        MinimumHandle = 0x1,
45
        MaximumHandle = 0x2
46
    };
47
    Q_DECLARE_FLAGS(Handles, Handle);
48

49
    ctkRangeSliderPrivate(ctkRangeSlider& object);
50
    void init();
51

52
    /// Return the handle at the given pos, or none if no handle is at the pos.
53
    /// If a handle is selected, handleRect is set to the handle rect.
54
    /// otherwise return NoHandle and handleRect is set to the combined rect of
55
    /// the min and max handles
56
    Handle handleAtPos(const QPoint& pos, QRect& handleRect)const;
57

58
    /// Copied verbatim from QSliderPrivate class (see QSlider.cpp)
59
    int pixelPosToRangeValue(int pos) const;
60
    int pixelPosFromRangeValue(int val) const;
61

62
    /// Draw the bottom and top sliders.
63
    void drawMinimumSlider(QStylePainter *painter) const;
64
    void drawMaximumSlider(QStylePainter *painter) const;
65

66
    /// End points of the range on the Model
67
    int m_MaximumValue;
68
    int m_MinimumValue;
69

70
    /// End points of the range on the GUI. This is synced with the model.
71
    int m_MaximumPosition;
72
    int m_MinimumPosition;
73

74
    /// Controls selected ?
75
    QStyle::SubControl m_MinimumSliderSelected;
76
    QStyle::SubControl m_MaximumSliderSelected;
77

78
    /// See QSliderPrivate::clickOffset.
79
    /// Overrides this ivar
80
    int m_SubclassClickOffset;
81

82
    /// See QSliderPrivate::position
83
    /// Overrides this ivar.
84
    int m_SubclassPosition;
85

86
    /// Original width between the 2 bounds before any moves
87
    float m_SubclassWidth;
88

89
    ctkRangeSliderPrivate::Handles m_SelectedHandles;
90

91
    /// When symmetricMoves is true, moving a handle will move the other handle
92
    /// symmetrically, otherwise the handles are independent.
93
    bool m_SymmetricMoves;
94

95
    QString m_HandleToolTip;
96

97
private:
98
    Q_DISABLE_COPY(ctkRangeSliderPrivate);
99
};
100

101
// --------------------------------------------------------------------------
102
ctkRangeSliderPrivate::ctkRangeSliderPrivate(ctkRangeSlider& object)
18✔
103
    : q_ptr(&object)
18✔
104
{
105
    this->m_MinimumValue = 0;
18✔
106
    this->m_MaximumValue = 100;
18✔
107
    this->m_MinimumPosition = 0;
18✔
108
    this->m_MaximumPosition = 100;
18✔
109
    this->m_MinimumSliderSelected = QStyle::SC_None;
18✔
110
    this->m_MaximumSliderSelected = QStyle::SC_None;
18✔
111
    this->m_SubclassClickOffset = 0;
18✔
112
    this->m_SubclassPosition = 0;
18✔
113
    this->m_SubclassWidth = 0.0;
18✔
114
    this->m_SelectedHandles = ctkRangeSliderPrivate::Handles();;
18✔
115
    this->m_SymmetricMoves = false;
18✔
116
}
18✔
117

118
// --------------------------------------------------------------------------
119
void ctkRangeSliderPrivate::init()
18✔
120
{
121
    Q_Q(ctkRangeSlider);
18✔
122
    this->m_MinimumValue = q->minimum();
18✔
123
    this->m_MaximumValue = q->maximum();
18✔
124
    this->m_MinimumPosition = q->minimum();
18✔
125
    this->m_MaximumPosition = q->maximum();
18✔
126
    q->connect(q, SIGNAL(rangeChanged(int,int)), q, SLOT(onRangeChanged(int,int)));
18✔
127
}
18✔
128

129
// --------------------------------------------------------------------------
130
ctkRangeSliderPrivate::Handle ctkRangeSliderPrivate::handleAtPos(const QPoint& pos, QRect& handleRect)const
×
131
{
132
    Q_Q(const ctkRangeSlider);
×
133

134
    QStyleOptionSlider option;
×
135
    q->initStyleOption(&option);
×
136

137
    // The functinos hitTestComplexControl only know about 1 handle. As we have
138
    // 2, we change the position of the handle and test if the pos correspond to
139
    // any of the 2 positions.
140

141
    // Test the MinimumHandle
142
    option.sliderPosition = this->m_MinimumPosition;
×
143
    option.sliderValue    = this->m_MinimumValue;
×
144

145
    //QStyle::SubControl minimumControl = q->style()->hitTestComplexControl(QStyle::CC_Slider, &option, pos, q);
146
    QRect minimumHandleRect = q->style()->subControlRect(QStyle::CC_Slider, &option, QStyle::SC_SliderHandle, q);
×
147

148
    // Test if the pos is under the Maximum handle
149
    option.sliderPosition = this->m_MaximumPosition;
×
150
    option.sliderValue    = this->m_MaximumValue;
×
151

152
    //QStyle::SubControl maximumControl = q->style()->hitTestComplexControl(QStyle::CC_Slider, &option, pos, q);
153
    QRect maximumHandleRect = q->style()->subControlRect(QStyle::CC_Slider, &option, QStyle::SC_SliderHandle, q);
×
154

155
    // The pos is above both handles, select the closest handle
156
/*
157
    if (minimumControl == QStyle::SC_SliderHandle &&
158
        maximumControl == QStyle::SC_SliderHandle)
159
    {
160
        int minDist = 0;
161
        int maxDist = 0;
162
        if (q->orientation() == Qt::Horizontal)
163
        {
164
            minDist = pos.x() - minimumHandleRect.left();
165
            maxDist = maximumHandleRect.right() - pos.x();
166
        }
167
        else //if (q->orientation() == Qt::Vertical)
168
        {
169
            minDist = minimumHandleRect.bottom() - pos.y();
170
            maxDist = pos.y() - maximumHandleRect.top();
171
        }
172
        Q_ASSERT(minDist >= 0 && maxDist >= 0);
173
        minimumControl = minDist < maxDist ? minimumControl : QStyle::SC_None;
174
    }
175
*/
176
    if (minimumHandleRect.contains(pos))
×
177
    {
178
        handleRect = minimumHandleRect;
×
179
        return MinimumHandle;
×
180
    }
181
    else if (maximumHandleRect.contains(pos))
×
182
    {
183
        handleRect = maximumHandleRect;
×
184
        return MaximumHandle;
×
185
    }
186
    handleRect = minimumHandleRect.united(maximumHandleRect);
×
187
    return NoHandle;
×
188
}
189

190
// --------------------------------------------------------------------------
191
// Copied verbatim from QSliderPrivate::pixelPosToRangeValue. See QSlider.cpp
192
//
193
int ctkRangeSliderPrivate::pixelPosToRangeValue(int pos) const
×
194
{
195
    Q_Q(const ctkRangeSlider);
×
196
    QStyleOptionSlider option;
×
197
    q->initStyleOption(&option);
×
198

199
    QRect gr = q->style()->subControlRect(QStyle::CC_Slider,
×
200
                                          &option,
201
                                          QStyle::SC_SliderGroove,
202
                                          q);
×
203
    QRect sr = q->style()->subControlRect(QStyle::CC_Slider,
×
204
                                          &option,
205
                                          QStyle::SC_SliderHandle,
206
                                          q);
×
207
    int sliderMin, sliderMax, sliderLength;
208
    if (option.orientation == Qt::Horizontal)
×
209
    {
210
        sliderLength = sr.width();
×
211
        sliderMin = gr.x();
×
212
        sliderMax = gr.right() - sliderLength + 1;
×
213
    }
214
    else
215
    {
216
        sliderLength = sr.height();
×
217
        sliderMin = gr.y();
×
218
        sliderMax = gr.bottom() - sliderLength + 1;
×
219
    }
220

221
    return QStyle::sliderValueFromPosition(q->minimum(),
×
222
                                           q->maximum(),
223
                                           pos - sliderMin,
224
                                           sliderMax - sliderMin,
225
                                           option.upsideDown);
×
226
}
227

228
//---------------------------------------------------------------------------
229
int ctkRangeSliderPrivate::pixelPosFromRangeValue(int val) const
×
230
{
231
    Q_Q(const ctkRangeSlider);
×
232
    QStyleOptionSlider option;
×
233
    q->initStyleOption(&option);
×
234

235
    QRect gr = q->style()->subControlRect(QStyle::CC_Slider,
×
236
                                          &option,
237
                                          QStyle::SC_SliderGroove,
238
                                          q);
×
239
    QRect sr = q->style()->subControlRect(QStyle::CC_Slider,
×
240
                                          &option,
241
                                          QStyle::SC_SliderHandle,
242
                                          q);
×
243
    int sliderMin, sliderMax, sliderLength;
244
    if (option.orientation == Qt::Horizontal)
×
245
    {
246
        sliderLength = sr.width();
×
247
        sliderMin = gr.x();
×
248
        sliderMax = gr.right() - sliderLength + 1;
×
249
    }
250
    else
251
    {
252
        sliderLength = sr.height();
×
253
        sliderMin = gr.y();
×
254
        sliderMax = gr.bottom() - sliderLength + 1;
×
255
    }
256

257
    return QStyle::sliderPositionFromValue(q->minimum(),
×
258
                                           q->maximum(),
259
                                           val,
260
                                           sliderMax - sliderMin,
261
                                           option.upsideDown) + sliderMin;
×
262
}
263

264
//---------------------------------------------------------------------------
265
// Draw slider at the bottom end of the range
266
void ctkRangeSliderPrivate::drawMinimumSlider(QStylePainter *painter) const
4✔
267
{
268
    Q_Q(const ctkRangeSlider);
4✔
269
    QStyleOptionSlider option;
4✔
270
    q->initMinimumSliderStyleOption(&option);
4✔
271

272
    option.subControls = QStyle::SC_SliderHandle;
4✔
273
    option.sliderValue = m_MinimumValue;
4✔
274
    option.sliderPosition = m_MinimumPosition;
4✔
275
    if (q->isMinimumSliderDown())
4✔
276
    {
277
        option.activeSubControls = QStyle::SC_SliderHandle;
×
278
        option.state |= QStyle::State_Sunken;
279
    }
280
#ifdef Q_OS_MAC
281
    // On mac style, drawing just the handle actually draws also the groove.
282
    QRect clip = q->style()->subControlRect(QStyle::CC_Slider, &option,
283
                                            QStyle::SC_SliderHandle, q);
284
    painter->setClipRect(clip);
285
#endif
286
    painter->drawComplexControl(QStyle::CC_Slider, option);
287
}
4✔
288

289
//---------------------------------------------------------------------------
290
// Draw slider at the top end of the range
291
void ctkRangeSliderPrivate::drawMaximumSlider(QStylePainter *painter) const
4✔
292
{
293
    Q_Q(const ctkRangeSlider);
4✔
294
    QStyleOptionSlider option;
4✔
295
    q->initMaximumSliderStyleOption(&option);
4✔
296

297
    option.subControls = QStyle::SC_SliderHandle;
4✔
298
    option.sliderValue = m_MaximumValue;
4✔
299
    option.sliderPosition = m_MaximumPosition;
4✔
300
    if (q->isMaximumSliderDown())
4✔
301
    {
302
        option.activeSubControls = QStyle::SC_SliderHandle;
×
303
        option.state |= QStyle::State_Sunken;
304
    }
305
#ifdef Q_OS_MAC
306
    // On mac style, drawing just the handle actually draws also the groove.
307
    QRect clip = q->style()->subControlRect(QStyle::CC_Slider, &option,
308
                                            QStyle::SC_SliderHandle, q);
309
    painter->setClipRect(clip);
310
#endif
311
    painter->drawComplexControl(QStyle::CC_Slider, option);
312
}
4✔
313

314
// --------------------------------------------------------------------------
315
ctkRangeSlider::ctkRangeSlider(QWidget *_parent)
9✔
316
    : QSlider(_parent)
317
    , d_ptr(new ctkRangeSliderPrivate(*this))
9✔
318
{
319
    Q_D(ctkRangeSlider);
320
    d->init();
9✔
321
}
9✔
322

323
// --------------------------------------------------------------------------
324
ctkRangeSlider::ctkRangeSlider(Qt::Orientation o,
9✔
325
                               QWidget *parentObject)
9✔
326
    : QSlider(o, parentObject)
327
    , d_ptr(new ctkRangeSliderPrivate(*this))
9✔
328
{
329
    Q_D(ctkRangeSlider);
330
    d->init();
9✔
331
}
9✔
332

333
// --------------------------------------------------------------------------
334
ctkRangeSlider::ctkRangeSlider(ctkRangeSliderPrivate *impl, QWidget *_parent)
×
335
    : QSlider(_parent)
336
    , d_ptr(impl)
×
337
{
338
    Q_D(ctkRangeSlider);
339
    d->init();
×
340
}
×
341

342
// --------------------------------------------------------------------------
343
ctkRangeSlider::ctkRangeSlider(ctkRangeSliderPrivate *impl, Qt::Orientation o,
×
344
                               QWidget *parentObject)
×
345
    : QSlider(o, parentObject)
346
    , d_ptr(impl)
×
347
{
348
    Q_D(ctkRangeSlider);
349
    d->init();
×
350
}
×
351

352
// --------------------------------------------------------------------------
353
ctkRangeSlider::~ctkRangeSlider()
36✔
354
{
355
}
36✔
356

357
// --------------------------------------------------------------------------
358
int ctkRangeSlider::minimumValue() const
×
359
{
360
    Q_D(const ctkRangeSlider);
361
    return d->m_MinimumValue;
×
362
}
363

364
// --------------------------------------------------------------------------
365
void ctkRangeSlider::setMinimumValue(int min)
×
366
{
367
    Q_D(ctkRangeSlider);
368
    this->setValues(min, qMax(d->m_MaximumValue,min));
×
369
}
×
370

371
// --------------------------------------------------------------------------
372
int ctkRangeSlider::maximumValue() const
×
373
{
374
    Q_D(const ctkRangeSlider);
375
    return d->m_MaximumValue;
×
376
}
377

378
// --------------------------------------------------------------------------
379
void ctkRangeSlider::setMaximumValue(int max)
×
380
{
381
    Q_D(ctkRangeSlider);
382
    this->setValues(qMin(d->m_MinimumValue, max), max);
×
383
}
×
384

385
// --------------------------------------------------------------------------
386
void ctkRangeSlider::setValues(int l, int u)
36✔
387
{
388
    Q_D(ctkRangeSlider);
389
    const int minValue = qBound(this->minimum(), qMin(l,u), this->maximum());
36✔
390
    const int maxValue = qBound(this->minimum(), qMax(l,u), this->maximum());
36✔
391
    bool emitMinValChanged = (minValue != d->m_MinimumValue);
36✔
392
    bool emitMaxValChanged = (maxValue != d->m_MaximumValue);
36✔
393

394
    d->m_MinimumValue = minValue;
36✔
395
    d->m_MaximumValue = maxValue;
36✔
396

397
    bool emitMinPosChanged = (minValue != d->m_MinimumPosition);
36✔
398
    bool emitMaxPosChanged = (maxValue != d->m_MaximumPosition);
36✔
399
    d->m_MinimumPosition = minValue;
36✔
400
    d->m_MaximumPosition = maxValue;
36✔
401

402
    if (isSliderDown())
36✔
403
    {
404
        if (emitMinPosChanged || emitMaxPosChanged)
×
405
        {
406
            emit positionsChanged(d->m_MinimumPosition, d->m_MaximumPosition);
×
407
        }
408
        if (emitMinPosChanged)
×
409
        {
410
            emit minimumPositionChanged(d->m_MinimumPosition);
×
411
        }
412
        if (emitMaxPosChanged)
×
413
        {
414
            emit maximumPositionChanged(d->m_MaximumPosition);
×
415
        }
416
    }
417
    if (emitMinValChanged || emitMaxValChanged)
36✔
418
    {
419
        emit valuesChanged(d->m_MinimumValue, d->m_MaximumValue);
18✔
420
    }
421
    if (emitMinValChanged)
36✔
422
    {
423
        emit minimumValueChanged(d->m_MinimumValue);
×
424
    }
425
    if (emitMaxValChanged)
36✔
426
    {
427
        emit maximumValueChanged(d->m_MaximumValue);
18✔
428
    }
429
    if (emitMinPosChanged || emitMaxPosChanged ||
36✔
430
        emitMinValChanged || emitMaxValChanged)
36✔
431
    {
432
        this->update();
18✔
433
    }
434
}
36✔
435

436
// --------------------------------------------------------------------------
437
int ctkRangeSlider::minimumPosition() const
2✔
438
{
439
    Q_D(const ctkRangeSlider);
440
    return d->m_MinimumPosition;
2✔
441
}
442

443
// --------------------------------------------------------------------------
444
int ctkRangeSlider::maximumPosition() const
2✔
445
{
446
    Q_D(const ctkRangeSlider);
447
    return d->m_MaximumPosition;
2✔
448
}
449

450
// --------------------------------------------------------------------------
451
void ctkRangeSlider::setMinimumPosition(int l)
×
452
{
453
    Q_D(const ctkRangeSlider);
454
    this->setPositions(l, qMax(l, d->m_MaximumPosition));
×
455
}
×
456

457
// --------------------------------------------------------------------------
458
void ctkRangeSlider::setMaximumPosition(int u)
18✔
459
{
460
    Q_D(const ctkRangeSlider);
461
    this->setPositions(qMin(d->m_MinimumPosition, u), u);
18✔
462
}
18✔
463

464
// --------------------------------------------------------------------------
465
void ctkRangeSlider::setPositions(int min, int max)
18✔
466
{
467
    Q_D(ctkRangeSlider);
468
    const int minPosition = qBound(this->minimum(), qMin(min, max), this->maximum());
18✔
469
    const int maxPosition = qBound(this->minimum(), qMax(min, max), this->maximum());
18✔
470

471
    bool emitMinPosChanged = (minPosition != d->m_MinimumPosition);
18✔
472
    bool emitMaxPosChanged = (maxPosition != d->m_MaximumPosition);
18✔
473

474
    if (!emitMinPosChanged && !emitMaxPosChanged)
18✔
475
    {
476
        return;
477
    }
478

479
    d->m_MinimumPosition = minPosition;
18✔
480
    d->m_MaximumPosition = maxPosition;
18✔
481

482
    if (!this->hasTracking())
18✔
483
    {
484
        this->update();
×
485
    }
486
    if (isSliderDown())
18✔
487
    {
488
        if (emitMinPosChanged)
×
489
        {
490
            emit minimumPositionChanged(d->m_MinimumPosition);
×
491
        }
492
        if (emitMaxPosChanged)
×
493
        {
494
            emit maximumPositionChanged(d->m_MaximumPosition);
×
495
        }
496
        if (emitMinPosChanged || emitMaxPosChanged)
×
497
        {
498
            emit positionsChanged(d->m_MinimumPosition, d->m_MaximumPosition);
×
499
        }
500
    }
501
    if (this->hasTracking())
18✔
502
    {
503
        this->triggerAction(SliderMove);
18✔
504
        this->setValues(d->m_MinimumPosition, d->m_MaximumPosition);
18✔
505
    }
506
}
507

508
// --------------------------------------------------------------------------
509
void ctkRangeSlider::setSymmetricMoves(bool symmetry)
×
510
{
511
    Q_D(ctkRangeSlider);
512
    d->m_SymmetricMoves = symmetry;
×
513
}
×
514

515
// --------------------------------------------------------------------------
516
bool ctkRangeSlider::symmetricMoves()const
×
517
{
518
    Q_D(const ctkRangeSlider);
519
    return d->m_SymmetricMoves;
×
520
}
521

522
// --------------------------------------------------------------------------
523
void ctkRangeSlider::onRangeChanged(int _minimum, int _maximum)
18✔
524
{
525
    Q_UNUSED(_minimum);
526
    Q_UNUSED(_maximum);
527
    Q_D(ctkRangeSlider);
528
    this->setValues(d->m_MinimumValue, d->m_MaximumValue);
18✔
529
}
18✔
530

531
// --------------------------------------------------------------------------
532
// Render
533
void ctkRangeSlider::paintEvent(QPaintEvent *)
4✔
534
{
535
    Q_D(ctkRangeSlider);
536
    QStyleOptionSlider option;
4✔
537
    this->initStyleOption(&option);
4✔
538

539
    QStylePainter painter(this);
4✔
540
    option.subControls = QStyle::SC_SliderGroove;
4✔
541
    // Move to minimum to not highlight the SliderGroove.
542
    // On mac style, drawing just the slider groove also draws the handles,
543
    // therefore we give a negative (outside of view) position.
544
    option.sliderValue = this->minimum() - this->maximum();
4✔
545
    option.sliderPosition = this->minimum() - this->maximum();
4✔
546
    painter.drawComplexControl(QStyle::CC_Slider, option);
547

548
    option.sliderPosition = d->m_MinimumPosition;
4✔
549
    const QRect lr = style()->subControlRect(QStyle::CC_Slider,
4✔
550
                                             &option,
551
                                             QStyle::SC_SliderHandle,
552
                                             this);
4✔
553
    option.sliderPosition = d->m_MaximumPosition;
4✔
554

555
    const QRect ur = style()->subControlRect(QStyle::CC_Slider,
4✔
556
                                             &option,
557
                                             QStyle::SC_SliderHandle,
558
                                             this);
4✔
559

560
    QRect sr = style()->subControlRect(QStyle::CC_Slider,
4✔
561
                                       &option,
562
                                       QStyle::SC_SliderGroove,
563
                                       this);
4✔
564
    QRect rangeBox;
4✔
565
    if (option.orientation == Qt::Horizontal)
4✔
566
    {
567
        rangeBox = QRect(
2✔
568
            QPoint(qMin(lr.center().x(), ur.center().x()), sr.center().y() - 2),
4✔
569
            QPoint(qMax(lr.center().x(), ur.center().x()), sr.center().y() + 1));
2✔
570
    }
571
    else
572
    {
573
        rangeBox = QRect(
2✔
574
            QPoint(sr.center().x() - 2, qMin(lr.center().y(), ur.center().y())),
2✔
575
            QPoint(sr.center().x() + 1, qMax(lr.center().y(), ur.center().y())));
2✔
576
    }
577

578
    // -----------------------------
579
    // Render the range
580
    //
581
    QRect groove = this->style()->subControlRect(QStyle::CC_Slider,
4✔
582
                                                 &option,
583
                                                 QStyle::SC_SliderGroove,
584
                                                 this);
4✔
585
    groove.adjust(0, 0, -1, 0);
586

587
    // Create default colors based on the transfer function.
588
    //
589
    QColor highlight = this->palette().color(QPalette::Highlight);
4✔
590
    QLinearGradient gradient;
8✔
591
    if (option.orientation == Qt::Horizontal)
4✔
592
    {
593
        gradient = QLinearGradient(groove.center().x(), groove.top(),
2✔
594
                                   groove.center().x(), groove.bottom());
2✔
595
    }
596
    else
597
    {
598
        gradient = QLinearGradient(groove.left(), groove.center().y(),
4✔
599
                                   groove.right(), groove.center().y());
600
    }
601

602
    // TODO: Set this based on the supplied transfer function
603
    //QColor l = Qt::darkGray;
604
    //QColor u = Qt::black;
605

606
    // Like Fusion Style to match QSlider
607
    gradient.setColorAt(0, highlight.darker(120));
4✔
608
    gradient.setColorAt(1, highlight.lighter(160));
4✔
609

610
    painter.setPen(QPen(highlight.darker(150), 0));
4✔
611
    painter.setBrush(gradient);
4✔
612
    painter.drawRect(rangeBox.intersected(groove));
4✔
613

614
    //  -----------------------------------
615
    // Render the sliders
616
    //
617
    if (this->isMinimumSliderDown())
4✔
618
    {
619
        painter.setClipRect(ur);
×
620
        d->drawMaximumSlider(&painter);
×
621
        painter.setClipRect(lr);
×
622
        d->drawMinimumSlider(&painter);
×
623
    }
624
    else
625
    {
626
        painter.setClipRect(lr);
4✔
627
        d->drawMinimumSlider(&painter);
4✔
628
        painter.setClipRect(ur);
4✔
629
        d->drawMaximumSlider(&painter);
4✔
630
    }
631
}
4✔
632

633
// --------------------------------------------------------------------------
634
// Standard Qt UI events
635
void ctkRangeSlider::mousePressEvent(QMouseEvent* mouseEvent)
×
636
{
637
    Q_D(ctkRangeSlider);
638
    if (minimum() == maximum() || (mouseEvent->buttons() ^ mouseEvent->button()))
×
639
    {
640
        mouseEvent->ignore();
641
        return;
×
642
    }
643
    int mepos = this->orientation() == Qt::Horizontal ?
×
644
                    mouseEvent->pos().x() : mouseEvent->pos().y();
645

646
    QStyleOptionSlider option;
×
647
    this->initStyleOption(&option);
×
648

649
    QRect handleRect;
×
650
    ctkRangeSliderPrivate::Handle handle_ = d->handleAtPos(mouseEvent->pos(), handleRect);
×
651

652
    if (handle_ != ctkRangeSliderPrivate::NoHandle)
×
653
    {
654
        d->m_SubclassPosition = (handle_ == ctkRangeSliderPrivate::MinimumHandle) ?
×
655
                                    d->m_MinimumPosition : d->m_MaximumPosition;
656

657
        // save the position of the mouse inside the handle for later
658
        d->m_SubclassClickOffset = mepos - (this->orientation() == Qt::Horizontal ?
×
659
                                                handleRect.left() : handleRect.top());
×
660

661
        this->setSliderDown(true);
×
662

663
        if (d->m_SelectedHandles != handle_)
×
664
        {
665
            d->m_SelectedHandles = handle_;
×
666
            this->update(handleRect);
×
667
        }
668
        // Accept the mouseEvent
669
        mouseEvent->accept();
670
        return;
×
671
    }
672

673
    // if we are here, no handles have been pressed
674
    // Check if we pressed on the groove between the 2 handles
675

676
    QStyle::SubControl control = this->style()->hitTestComplexControl(QStyle::CC_Slider, &option, mouseEvent->pos(), this);
×
677
    QRect sr = style()->subControlRect(QStyle::CC_Slider, &option, QStyle::SC_SliderGroove, this);
×
678
    int minCenter = (this->orientation() == Qt::Horizontal ?
×
679
                         handleRect.left() : handleRect.top());
×
680
    int maxCenter = (this->orientation() == Qt::Horizontal ?
×
681
                         handleRect.right() : handleRect.bottom());
×
682
    if (control == QStyle::SC_SliderGroove &&
×
683
        mepos > minCenter && mepos < maxCenter)
×
684
    {
685
        // warning lost of precision it might be fatal
686
        d->m_SubclassPosition = (d->m_MinimumPosition + d->m_MaximumPosition) / 2.;
×
687
        d->m_SubclassClickOffset = mepos - d->pixelPosFromRangeValue(d->m_SubclassPosition);
×
688
        d->m_SubclassWidth = (d->m_MaximumPosition - d->m_MinimumPosition) / 2;
×
689
        qMax(d->m_SubclassPosition - d->m_MinimumPosition, d->m_MaximumPosition - d->m_SubclassPosition);
690
        this->setSliderDown(true);
×
691
        if (!this->isMinimumSliderDown() || !this->isMaximumSliderDown())
×
692
        {
693
            d->m_SelectedHandles =
×
694
                QFlags<ctkRangeSliderPrivate::Handle>(ctkRangeSliderPrivate::MinimumHandle) |
695
                QFlags<ctkRangeSliderPrivate::Handle>(ctkRangeSliderPrivate::MaximumHandle);
696
            this->update(handleRect.united(sr));
×
697
        }
698
        mouseEvent->accept();
699
        return;
×
700
    }
701
    mouseEvent->ignore();
702
}
703

704
// --------------------------------------------------------------------------
705
// Standard Qt UI events
706
void ctkRangeSlider::mouseMoveEvent(QMouseEvent* mouseEvent)
×
707
{
708
    Q_D(ctkRangeSlider);
709
    if (!d->m_SelectedHandles)
×
710
    {
711
        mouseEvent->ignore();
712
        return;
×
713
    }
714
    int mepos = this->orientation() == Qt::Horizontal ?
×
715
                    mouseEvent->pos().x() : mouseEvent->pos().y();
716

717
    QStyleOptionSlider option;
×
718
    this->initStyleOption(&option);
×
719

720
    const int m = style()->pixelMetric(QStyle::PM_MaximumDragDistance, &option, this);
×
721

722
    int newPosition = d->pixelPosToRangeValue(mepos - d->m_SubclassClickOffset);
×
723

724
    if (m >= 0)
×
725
    {
726
        const QRect r = rect().adjusted(-m, -m, m, m);
×
727
        if (!r.contains(mouseEvent->pos()))
×
728
        {
729
            newPosition = d->m_SubclassPosition;
×
730
        }
731
    }
732

733
    // Only the lower/left slider is down
734
    if (this->isMinimumSliderDown() && !this->isMaximumSliderDown())
×
735
    {
736
        double newMinPos = qMin(newPosition,d->m_MaximumPosition);
×
737
        this->setPositions(newMinPos, d->m_MaximumPosition +
×
738
                                          (d->m_SymmetricMoves ? d->m_MinimumPosition - newMinPos : 0));
×
739
    }
740
    // Only the upper/right slider is down
741
    else if (this->isMaximumSliderDown() && !this->isMinimumSliderDown())
×
742
    {
743
        double newMaxPos = qMax(d->m_MinimumPosition, newPosition);
×
744
        this->setPositions(d->m_MinimumPosition -
×
745
                           (d->m_SymmetricMoves ? newMaxPos - d->m_MaximumPosition: 0),
×
746
                           newMaxPos);
747
    }
748
    // Both handles are down (the user clicked in between the handles)
749
    else if (this->isMinimumSliderDown() && this->isMaximumSliderDown())
×
750
    {
751
        this->setPositions(newPosition - static_cast<int>(d->m_SubclassWidth),
×
752
                           newPosition + static_cast<int>(d->m_SubclassWidth + .5));
×
753
    }
754
    mouseEvent->accept();
755
}
756

757
// --------------------------------------------------------------------------
758
// Standard Qt UI mouseEvents
759
void ctkRangeSlider::mouseReleaseEvent(QMouseEvent* mouseEvent)
×
760
{
761
    Q_D(ctkRangeSlider);
762
    this->QSlider::mouseReleaseEvent(mouseEvent);
×
763

764
    setSliderDown(false);
×
765
    d->m_SelectedHandles = ctkRangeSliderPrivate::Handles();
×
766

767
    this->update();
×
768
}
×
769

770
// --------------------------------------------------------------------------
771
bool ctkRangeSlider::isMinimumSliderDown()const
8✔
772
{
773
    Q_D(const ctkRangeSlider);
774
    return d->m_SelectedHandles & ctkRangeSliderPrivate::MinimumHandle;
8✔
775
}
776

777
// --------------------------------------------------------------------------
778
bool ctkRangeSlider::isMaximumSliderDown()const
4✔
779
{
780
    Q_D(const ctkRangeSlider);
781
    return d->m_SelectedHandles & ctkRangeSliderPrivate::MaximumHandle;
4✔
782
}
783

784
// --------------------------------------------------------------------------
785
void ctkRangeSlider::initMinimumSliderStyleOption(QStyleOptionSlider* option) const
4✔
786
{
787
    this->initStyleOption(option);
4✔
788
}
4✔
789

790
// --------------------------------------------------------------------------
791
void ctkRangeSlider::initMaximumSliderStyleOption(QStyleOptionSlider* option) const
4✔
792
{
793
    this->initStyleOption(option);
4✔
794
}
4✔
795

796
// --------------------------------------------------------------------------
797
QString ctkRangeSlider::handleToolTip()const
×
798
{
799
    Q_D(const ctkRangeSlider);
800
    return d->m_HandleToolTip;
×
801
}
802

803
// --------------------------------------------------------------------------
804
void ctkRangeSlider::setHandleToolTip(const QString& _toolTip)
×
805
{
806
    Q_D(ctkRangeSlider);
807
    d->m_HandleToolTip = _toolTip;
×
808
}
×
809

810
// --------------------------------------------------------------------------
811
bool ctkRangeSlider::event(QEvent* _event)
50✔
812
{
813
    Q_D(ctkRangeSlider);
814
    switch(_event->type())
50✔
815
    {
816
    case QEvent::ToolTip:
×
817
    {
818
        QHelpEvent* helpEvent = static_cast<QHelpEvent*>(_event);
819
        QStyleOptionSlider opt;
×
820
        // Test the MinimumHandle
821
        opt.sliderPosition = d->m_MinimumPosition;
×
822
        opt.sliderValue = d->m_MinimumValue;
×
823
        this->initStyleOption(&opt);
×
824
        QStyle::SubControl hoveredControl =
825
            this->style()->hitTestComplexControl(
×
826
                QStyle::CC_Slider, &opt, helpEvent->pos(), this);
×
827
        if (!d->m_HandleToolTip.isEmpty() &&
×
828
            hoveredControl == QStyle::SC_SliderHandle)
829
        {
830
            QToolTip::showText(helpEvent->globalPos(), d->m_HandleToolTip.arg(this->minimumValue()));
×
831
            _event->accept();
832
            return true;
×
833
        }
834
        // Test the MaximumHandle
835
        opt.sliderPosition = d->m_MaximumPosition;
×
836
        opt.sliderValue = d->m_MaximumValue;
×
837
        this->initStyleOption(&opt);
×
838
        hoveredControl = this->style()->hitTestComplexControl(
×
839
            QStyle::CC_Slider, &opt, helpEvent->pos(), this);
×
840
        if (!d->m_HandleToolTip.isEmpty() &&
×
841
            hoveredControl == QStyle::SC_SliderHandle)
842
        {
843
            QToolTip::showText(helpEvent->globalPos(), d->m_HandleToolTip.arg(this->maximumValue()));
×
844
            _event->accept();
845
            return true;
×
846
        }
847
    }
848
    default:
849
        break;
850
    }
851
    return this->Superclass::event(_event);
50✔
852
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc