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

mcallegari / qlcplus / 18357067171

08 Oct 2025 08:16PM UTC coverage: 34.26% (+2.2%) from 32.066%
18357067171

push

github

mcallegari
Merge branch 'master' into filedialog

1282 of 4424 new or added lines in 152 files covered. (28.98%)

1342 existing lines in 152 files now uncovered.

17704 of 51675 relevant lines covered (34.26%)

19430.31 hits per line

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

43.54
/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
35
{
36
    Q_DECLARE_PUBLIC(ctkRangeSlider);
26✔
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
{
NEW
132
    Q_Q(const ctkRangeSlider);
×
133

NEW
134
    QStyleOptionSlider option;
×
NEW
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
NEW
142
    option.sliderPosition = this->m_MinimumPosition;
×
NEW
143
    option.sliderValue    = this->m_MinimumValue;
×
144

145
    //QStyle::SubControl minimumControl = q->style()->hitTestComplexControl(QStyle::CC_Slider, &option, pos, q);
NEW
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
NEW
149
    option.sliderPosition = this->m_MaximumPosition;
×
NEW
150
    option.sliderValue    = this->m_MaximumValue;
×
151

152
    //QStyle::SubControl maximumControl = q->style()->hitTestComplexControl(QStyle::CC_Slider, &option, pos, q);
NEW
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
*/
NEW
176
    if (minimumHandleRect.contains(pos))
×
177
    {
NEW
178
        handleRect = minimumHandleRect;
×
NEW
179
        return MinimumHandle;
×
180
    }
NEW
181
    else if (maximumHandleRect.contains(pos))
×
182
    {
NEW
183
        handleRect = maximumHandleRect;
×
NEW
184
        return MaximumHandle;
×
185
    }
NEW
186
    handleRect = minimumHandleRect.united(maximumHandleRect);
×
NEW
187
    return NoHandle;
×
UNCOV
188
}
×
189

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

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

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

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

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

NEW
257
    return QStyle::sliderPositionFromValue(q->minimum(),
×
258
                                           q->maximum(),
259
                                           val,
260
                                           sliderMax - sliderMin,
NEW
261
                                           option.upsideDown) + sliderMin;
×
UNCOV
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
    {
NEW
277
        option.activeSubControls = QStyle::SC_SliderHandle;
×
NEW
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);
4✔
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
    {
NEW
302
        option.activeSubControls = QStyle::SC_SliderHandle;
×
NEW
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);
4✔
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);
9✔
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);
9✔
330
    d->init();
9✔
331
}
9✔
332

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

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

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

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

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

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

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

385
// --------------------------------------------------------------------------
386
void ctkRangeSlider::setValues(int l, int u)
36✔
387
{
388
    Q_D(ctkRangeSlider);
36✔
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
    {
NEW
404
        if (emitMinPosChanged || emitMaxPosChanged)
×
405
        {
NEW
406
            emit positionsChanged(d->m_MinimumPosition, d->m_MaximumPosition);
×
407
        }
NEW
408
        if (emitMinPosChanged)
×
409
        {
NEW
410
            emit minimumPositionChanged(d->m_MinimumPosition);
×
411
        }
NEW
412
        if (emitMaxPosChanged)
×
413
        {
NEW
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
    {
NEW
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);
2✔
440
    return d->m_MinimumPosition;
2✔
441
}
442

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

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

457
// --------------------------------------------------------------------------
458
void ctkRangeSlider::setMaximumPosition(int u)
18✔
459
{
460
    Q_D(const ctkRangeSlider);
18✔
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);
18✔
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
    {
NEW
476
        return;
×
477
    }
478

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

482
    if (!this->hasTracking())
18✔
483
    {
NEW
484
        this->update();
×
485
    }
486
    if (isSliderDown())
18✔
487
    {
NEW
488
        if (emitMinPosChanged)
×
489
        {
NEW
490
            emit minimumPositionChanged(d->m_MinimumPosition);
×
491
        }
NEW
492
        if (emitMaxPosChanged)
×
493
        {
NEW
494
            emit maximumPositionChanged(d->m_MaximumPosition);
×
495
        }
NEW
496
        if (emitMinPosChanged || emitMaxPosChanged)
×
497
        {
NEW
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
{
NEW
511
    Q_D(ctkRangeSlider);
×
NEW
512
    d->m_SymmetricMoves = symmetry;
×
UNCOV
513
}
×
514

515
// --------------------------------------------------------------------------
516
bool ctkRangeSlider::symmetricMoves()const
×
517
{
NEW
518
    Q_D(const ctkRangeSlider);
×
NEW
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);
18✔
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);
4✔
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);
4✔
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);
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);
559

560
    QRect sr = style()->subControlRect(QStyle::CC_Slider,
4✔
561
                                       &option,
562
                                       QStyle::SC_SliderGroove,
563
                                       this);
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),
2✔
569
            QPoint(qMax(lr.center().x(), ur.center().x()), sr.center().y() + 1));
4✔
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())));
4✔
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);
585
    groove.adjust(0, 0, -1, 0);
4✔
586

587
    // Create default colors based on the transfer function.
588
    //
589
    QColor highlight = this->palette().color(QPalette::Highlight);
4✔
590
    QLinearGradient gradient;
4✔
591
    if (option.orientation == Qt::Horizontal)
4✔
592
    {
593
        gradient = QLinearGradient(groove.center().x(), groove.top(),
4✔
594
                                   groove.center().x(), groove.bottom());
6✔
595
    }
596
    else
597
    {
598
        gradient = QLinearGradient(groove.left(), groove.center().y(),
4✔
599
                                   groove.right(), groove.center().y());
6✔
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
    {
NEW
619
        painter.setClipRect(ur);
×
NEW
620
        d->drawMaximumSlider(&painter);
×
NEW
621
        painter.setClipRect(lr);
×
NEW
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
{
NEW
637
    Q_D(ctkRangeSlider);
×
NEW
638
    if (minimum() == maximum() || (mouseEvent->buttons() ^ mouseEvent->button()))
×
639
    {
NEW
640
        mouseEvent->ignore();
×
NEW
641
        return;
×
642
    }
NEW
643
    int mepos = this->orientation() == Qt::Horizontal ?
×
NEW
644
                    mouseEvent->pos().x() : mouseEvent->pos().y();
×
645

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

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

NEW
652
    if (handle_ != ctkRangeSliderPrivate::NoHandle)
×
653
    {
NEW
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
NEW
658
        d->m_SubclassClickOffset = mepos - (this->orientation() == Qt::Horizontal ?
×
NEW
659
                                                handleRect.left() : handleRect.top());
×
660

NEW
661
        this->setSliderDown(true);
×
662

NEW
663
        if (d->m_SelectedHandles != handle_)
×
664
        {
NEW
665
            d->m_SelectedHandles = handle_;
×
NEW
666
            this->update(handleRect);
×
667
        }
668
        // Accept the mouseEvent
NEW
669
        mouseEvent->accept();
×
NEW
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

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

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

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

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

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

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

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

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

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

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

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

777
// --------------------------------------------------------------------------
778
bool ctkRangeSlider::isMaximumSliderDown()const
4✔
779
{
780
    Q_D(const ctkRangeSlider);
4✔
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
{
NEW
799
    Q_D(const ctkRangeSlider);
×
NEW
800
    return d->m_HandleToolTip;
×
801
}
802

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

810
// --------------------------------------------------------------------------
811
bool ctkRangeSlider::event(QEvent* _event)
50✔
812
{
813
    Q_D(ctkRangeSlider);
50✔
814
    switch(_event->type())
50✔
815
    {
816
    case QEvent::ToolTip:
×
817
    {
NEW
818
        QHelpEvent* helpEvent = static_cast<QHelpEvent*>(_event);
×
NEW
819
        QStyleOptionSlider opt;
×
820
        // Test the MinimumHandle
NEW
821
        opt.sliderPosition = d->m_MinimumPosition;
×
NEW
822
        opt.sliderValue = d->m_MinimumValue;
×
NEW
823
        this->initStyleOption(&opt);
×
824
        QStyle::SubControl hoveredControl =
NEW
825
            this->style()->hitTestComplexControl(
×
826
                QStyle::CC_Slider, &opt, helpEvent->pos(), this);
NEW
827
        if (!d->m_HandleToolTip.isEmpty() &&
×
828
            hoveredControl == QStyle::SC_SliderHandle)
829
        {
NEW
830
            QToolTip::showText(helpEvent->globalPos(), d->m_HandleToolTip.arg(this->minimumValue()));
×
NEW
831
            _event->accept();
×
NEW
832
            return true;
×
833
        }
834
        // Test the MaximumHandle
NEW
835
        opt.sliderPosition = d->m_MaximumPosition;
×
NEW
836
        opt.sliderValue = d->m_MaximumValue;
×
NEW
837
        this->initStyleOption(&opt);
×
NEW
838
        hoveredControl = this->style()->hitTestComplexControl(
×
839
            QStyle::CC_Slider, &opt, helpEvent->pos(), this);
NEW
840
        if (!d->m_HandleToolTip.isEmpty() &&
×
841
            hoveredControl == QStyle::SC_SliderHandle)
842
        {
NEW
843
            QToolTip::showText(helpEvent->globalPos(), d->m_HandleToolTip.arg(this->maximumValue()));
×
NEW
844
            _event->accept();
×
NEW
845
            return true;
×
846
        }
NEW
847
    }
×
848
    default:
849
        break;
50✔
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