849 lines
28 KiB
C++
Executable File
849 lines
28 KiB
C++
Executable File
/**
|
|
* @RDE_HEADER@
|
|
* A slider that has a minimum and a maximum value, thus can define a range within a range.
|
|
* That could be a a snippet out of a video, a time range within an exercise...
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
*
|
|
* Created by Malte Diers, NLE, 2020-01-06
|
|
*/
|
|
|
|
/*=========================================================================
|
|
|
|
Library: CTK
|
|
|
|
Copyright (c) Kitware Inc.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0.txt
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
|
|
=========================================================================*/
|
|
|
|
#include "lts_range_slider.h"
|
|
|
|
// Qt includes
|
|
#include <QDebug>
|
|
#include <QMouseEvent>
|
|
#include <QKeyEvent>
|
|
#include <QStyleOptionSlider>
|
|
#include <QStylePainter>
|
|
#include <QStyle>
|
|
#include <QToolTip>
|
|
|
|
namespace LtsWidgets
|
|
{
|
|
|
|
class LtsRangeSliderPrivate
|
|
{
|
|
Q_DECLARE_PUBLIC(LtsRangeSlider)
|
|
protected:
|
|
LtsRangeSlider *const q_ptr;
|
|
public:
|
|
/// Boolean indicates the selected handle
|
|
/// True for the minimum range handle, false for the maximum range handle
|
|
enum Handle
|
|
{
|
|
NoHandle = 0x0,
|
|
MinimumHandle = 0x1,
|
|
MaximumHandle = 0x2
|
|
};
|
|
Q_DECLARE_FLAGS(Handles, Handle)
|
|
|
|
explicit LtsRangeSliderPrivate(LtsRangeSlider &object);
|
|
void init();
|
|
|
|
/// Return the handle at the given pos, or none if no handle is at the pos.
|
|
/// If a handle is selected, handleRect is set to the handle rect.
|
|
/// otherwise return NoHandle and handleRect is set to the combined rect of
|
|
/// the min and max handles
|
|
Handle handleAtPos(const QPoint &pos, QRect &handleRect) const;
|
|
|
|
/// Copied verbatim from QSliderPrivate class (see QSlider.cpp)
|
|
int pixelPosToRangeValue(int pos) const;
|
|
int pixelPosFromRangeValue(int val) const;
|
|
|
|
/// Draw the bottom and top sliders.
|
|
void drawMinimumSlider(QStylePainter *painter) const;
|
|
void drawMaximumSlider(QStylePainter *painter) const;
|
|
|
|
/// End points of the range on the Model
|
|
int m_minimumValue = 0;
|
|
int m_maximumValue = 100;
|
|
|
|
/// End points of the range on the GUI. This is synced with the model.
|
|
int m_minimumPosition = 0;
|
|
int m_maximumPosition = 100;
|
|
|
|
/// Controls selected ?
|
|
QStyle::SubControl m_minimumSliderSelected = QStyle::SC_None;
|
|
QStyle::SubControl m_maximumSliderSelected = QStyle::SC_None;
|
|
|
|
/// See QSliderPrivate::clickOffset.
|
|
/// Overrides this ivar
|
|
int m_subclassClickOffset = 0;
|
|
|
|
/// See QSliderPrivate::position
|
|
/// Overrides this ivar.
|
|
int m_subclassPosition = 0;
|
|
|
|
/// Original width between the 2 bounds before any moves
|
|
float m_subclassWidth = 0;
|
|
|
|
LtsRangeSliderPrivate::Handles m_selectedHandles = {};
|
|
|
|
/// When symmetricMoves is true, moving a handle will move the other handle
|
|
/// symmetrically, otherwise the handles are independent.
|
|
bool m_symmetricMoves = false;
|
|
|
|
QString m_handleToolTip;
|
|
|
|
private:
|
|
Q_DISABLE_COPY(LtsRangeSliderPrivate)
|
|
};
|
|
|
|
// --------------------------------------------------------------------------
|
|
LtsRangeSliderPrivate::LtsRangeSliderPrivate(LtsRangeSlider &object)
|
|
: q_ptr(&object)
|
|
{}
|
|
|
|
// --------------------------------------------------------------------------
|
|
void LtsRangeSliderPrivate::init()
|
|
{
|
|
Q_Q(LtsRangeSlider);
|
|
m_minimumValue = q->minimum();
|
|
m_maximumValue = q->maximum();
|
|
m_minimumPosition = q->minimum();
|
|
m_maximumPosition = q->maximum();
|
|
q->connect(q, &LtsRangeSlider::rangeChanged, q, &LtsRangeSlider::onRangeChanged);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
LtsRangeSliderPrivate::Handle LtsRangeSliderPrivate::handleAtPos(const QPoint &pos, QRect &handleRect)const
|
|
{
|
|
Q_Q(const LtsRangeSlider);
|
|
|
|
QStyleOptionSlider option;
|
|
q->initStyleOption(&option);
|
|
|
|
// The functinos hitTestComplexControl only know about 1 handle. As we have
|
|
// 2, we change the position of the handle and test if the pos correspond to
|
|
// any of the 2 positions.
|
|
|
|
// Test the MinimumHandle
|
|
option.sliderPosition = m_minimumPosition;
|
|
option.sliderValue = m_minimumValue;
|
|
|
|
QStyle::SubControl minimumControl = q->style()->hitTestComplexControl(
|
|
QStyle::CC_Slider, &option, pos, q);
|
|
QRect minimumHandleRect = q->style()->subControlRect(
|
|
QStyle::CC_Slider, &option, QStyle::SC_SliderHandle, q);
|
|
|
|
// Test if the pos is under the Maximum handle
|
|
option.sliderPosition = m_maximumPosition;
|
|
option.sliderValue = m_maximumValue;
|
|
|
|
QStyle::SubControl maximumControl = q->style()->hitTestComplexControl(QStyle::CC_Slider, &option, pos, q);
|
|
QRect maximumHandleRect = q->style()->subControlRect(QStyle::CC_Slider, &option, QStyle::SC_SliderHandle, q);
|
|
|
|
// The pos is above both handles, select the closest handle
|
|
if (minimumControl == QStyle::SC_SliderHandle &&
|
|
maximumControl == QStyle::SC_SliderHandle)
|
|
{
|
|
int minDist = 0;
|
|
int maxDist = 0;
|
|
if (q->orientation() == Qt::Horizontal)
|
|
{
|
|
minDist = pos.x() - minimumHandleRect.left();
|
|
maxDist = maximumHandleRect.right() - pos.x();
|
|
}
|
|
else //if (q->orientation() == Qt::Vertical)
|
|
{
|
|
minDist = minimumHandleRect.bottom() - pos.y();
|
|
maxDist = pos.y() - maximumHandleRect.top();
|
|
}
|
|
Q_ASSERT(minDist >= 0 && maxDist >= 0);
|
|
minimumControl = minDist < maxDist ? minimumControl : QStyle::SC_None;
|
|
}
|
|
|
|
if (maximumControl == QStyle::SC_SliderHandle)
|
|
{
|
|
handleRect = maximumHandleRect;
|
|
return MaximumHandle;
|
|
}
|
|
else if (minimumControl == QStyle::SC_SliderHandle)
|
|
{
|
|
handleRect = minimumHandleRect;
|
|
return MinimumHandle;
|
|
}
|
|
|
|
handleRect = minimumHandleRect.united(maximumHandleRect);
|
|
return NoHandle;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Copied verbatim from QSliderPrivate::pixelPosToRangeValue. See QSlider.cpp
|
|
//
|
|
int LtsRangeSliderPrivate::pixelPosToRangeValue(int pos) const
|
|
{
|
|
Q_Q(const LtsRangeSlider);
|
|
QStyleOptionSlider option;
|
|
q->initStyleOption(&option);
|
|
|
|
QRect gr = q->style()->subControlRect(QStyle::CC_Slider,
|
|
&option,
|
|
QStyle::SC_SliderGroove,
|
|
q);
|
|
QRect sr = q->style()->subControlRect(QStyle::CC_Slider,
|
|
&option,
|
|
QStyle::SC_SliderHandle,
|
|
q);
|
|
int sliderMin, sliderMax, sliderLength;
|
|
if (option.orientation == Qt::Horizontal)
|
|
{
|
|
sliderLength = sr.width();
|
|
sliderMin = gr.x();
|
|
sliderMax = gr.right() - sliderLength + 1;
|
|
}
|
|
else
|
|
{
|
|
sliderLength = sr.height();
|
|
sliderMin = gr.y();
|
|
sliderMax = gr.bottom() - sliderLength + 1;
|
|
}
|
|
|
|
return QStyle::sliderValueFromPosition(q->minimum(),
|
|
q->maximum(),
|
|
pos - sliderMin,
|
|
sliderMax - sliderMin,
|
|
option.upsideDown);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
int LtsRangeSliderPrivate::pixelPosFromRangeValue(int val) const
|
|
{
|
|
Q_Q(const LtsRangeSlider);
|
|
QStyleOptionSlider option;
|
|
q->initStyleOption(&option);
|
|
|
|
QRect gr = q->style()->subControlRect(QStyle::CC_Slider,
|
|
&option,
|
|
QStyle::SC_SliderGroove,
|
|
q);
|
|
QRect sr = q->style()->subControlRect(QStyle::CC_Slider,
|
|
&option,
|
|
QStyle::SC_SliderHandle,
|
|
q);
|
|
int sliderMin, sliderMax, sliderLength;
|
|
if (option.orientation == Qt::Horizontal)
|
|
{
|
|
sliderLength = sr.width();
|
|
sliderMin = gr.x();
|
|
sliderMax = gr.right() - sliderLength + 1;
|
|
}
|
|
else
|
|
{
|
|
sliderLength = sr.height();
|
|
sliderMin = gr.y();
|
|
sliderMax = gr.bottom() - sliderLength + 1;
|
|
}
|
|
|
|
return QStyle::sliderPositionFromValue(q->minimum(),
|
|
q->maximum(),
|
|
val,
|
|
sliderMax - sliderMin,
|
|
option.upsideDown) + sliderMin;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Draw slider at the bottom end of the range
|
|
void LtsRangeSliderPrivate::drawMinimumSlider(QStylePainter *painter) const
|
|
{
|
|
Q_Q(const LtsRangeSlider);
|
|
QStyleOptionSlider option;
|
|
q->initMinimumSliderStyleOption(&option);
|
|
|
|
option.subControls = QStyle::SC_SliderHandle;
|
|
option.sliderValue = m_minimumValue;
|
|
option.sliderPosition = m_minimumPosition;
|
|
if (q->isMinimumSliderDown())
|
|
{
|
|
option.activeSubControls = QStyle::SC_SliderHandle;
|
|
option.state |= QStyle::State_Sunken;
|
|
}
|
|
#ifdef Q_OS_MAC
|
|
// On mac style, drawing just the handle actually draws also the groove.
|
|
QRect clip = q->style()->subControlRect(QStyle::CC_Slider, &option,
|
|
QStyle::SC_SliderHandle, q);
|
|
painter->setClipRect(clip);
|
|
#endif
|
|
painter->drawComplexControl(QStyle::CC_Slider, option);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Draw slider at the top end of the range
|
|
void LtsRangeSliderPrivate::drawMaximumSlider(QStylePainter *painter) const
|
|
{
|
|
Q_Q(const LtsRangeSlider);
|
|
QStyleOptionSlider option;
|
|
q->initMaximumSliderStyleOption(&option);
|
|
|
|
option.subControls = QStyle::SC_SliderHandle;
|
|
option.sliderValue = m_maximumValue;
|
|
option.sliderPosition = m_maximumPosition;
|
|
if (q->isMaximumSliderDown())
|
|
{
|
|
option.activeSubControls = QStyle::SC_SliderHandle;
|
|
option.state |= QStyle::State_Sunken;
|
|
}
|
|
#ifdef Q_OS_MAC
|
|
// On mac style, drawing just the handle actually draws also the groove.
|
|
QRect clip = q->style()->subControlRect(QStyle::CC_Slider, &option,
|
|
QStyle::SC_SliderHandle, q);
|
|
painter->setClipRect(clip);
|
|
#endif
|
|
painter->drawComplexControl(QStyle::CC_Slider, option);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
LtsRangeSlider::LtsRangeSlider(Qt::Orientation o,
|
|
QWidget *parent)
|
|
: QSlider(o, parent)
|
|
, d_ptr(new LtsRangeSliderPrivate(*this))
|
|
{
|
|
Q_D(LtsRangeSlider);
|
|
d->init();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
LtsRangeSlider::LtsRangeSlider(LtsRangeSliderPrivate *impl, Qt::Orientation o,
|
|
QWidget *parent)
|
|
: QSlider(o, parent)
|
|
, d_ptr(impl)
|
|
{
|
|
Q_D(LtsRangeSlider);
|
|
d->init();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
LtsRangeSlider::~LtsRangeSlider()
|
|
{
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
int LtsRangeSlider::minimumValue() const
|
|
{
|
|
Q_D(const LtsRangeSlider);
|
|
return d->m_minimumValue;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
void LtsRangeSlider::setMinimumValue(int min)
|
|
{
|
|
Q_D(LtsRangeSlider);
|
|
setValues(min, qMax(d->m_maximumValue, min));
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
int LtsRangeSlider::maximumValue() const
|
|
{
|
|
Q_D(const LtsRangeSlider);
|
|
return d->m_maximumValue;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
void LtsRangeSlider::setMaximumValue(int max)
|
|
{
|
|
Q_D(LtsRangeSlider);
|
|
setValues(qMin(d->m_minimumValue, max), max);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
void LtsRangeSlider::setValues(int min, int max)
|
|
{
|
|
Q_D(LtsRangeSlider);
|
|
const int minValue = qBound(minimum(), qMin(min, max), maximum());
|
|
const int maxValue = qBound(minimum(), qMax(min, max), maximum());
|
|
bool emitMinValChanged = (minValue != d->m_minimumValue);
|
|
bool emitMaxValChanged = (maxValue != d->m_maximumValue);
|
|
|
|
d->m_minimumValue = minValue;
|
|
d->m_maximumValue = maxValue;
|
|
|
|
bool emitMinPosChanged = (minValue != d->m_minimumPosition);
|
|
bool emitMaxPosChanged = (maxValue != d->m_maximumPosition);
|
|
d->m_minimumPosition = minValue;
|
|
d->m_maximumPosition = maxValue;
|
|
|
|
if (isSliderDown())
|
|
{
|
|
if (emitMinPosChanged || emitMaxPosChanged)
|
|
emit positionsChanged(d->m_minimumPosition, d->m_maximumPosition);
|
|
if (emitMinPosChanged)
|
|
emit minimumPositionChanged(d->m_minimumPosition);
|
|
if (emitMaxPosChanged)
|
|
emit maximumPositionChanged(d->m_maximumPosition);
|
|
}
|
|
if (emitMinValChanged || emitMaxValChanged)
|
|
emit valuesChanged(d->m_minimumValue, d->m_maximumValue);
|
|
|
|
if (emitMinValChanged)
|
|
emit minimumValueChanged(d->m_minimumValue);
|
|
|
|
if (emitMaxValChanged)
|
|
emit maximumValueChanged(d->m_maximumValue);
|
|
|
|
if (emitMinPosChanged || emitMaxPosChanged ||
|
|
emitMinValChanged || emitMaxValChanged)
|
|
update();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
int LtsRangeSlider::minimumPosition() const
|
|
{
|
|
Q_D(const LtsRangeSlider);
|
|
return d->m_minimumPosition;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
int LtsRangeSlider::maximumPosition() const
|
|
{
|
|
Q_D(const LtsRangeSlider);
|
|
return d->m_maximumPosition;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
void LtsRangeSlider::setMinimumPosition(int min)
|
|
{
|
|
Q_D(const LtsRangeSlider);
|
|
setPositions(min, qMax(min, d->m_maximumPosition));
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
void LtsRangeSlider::setMaximumPosition(int max)
|
|
{
|
|
Q_D(const LtsRangeSlider);
|
|
setPositions(qMin(d->m_minimumPosition, max), max);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
void LtsRangeSlider::setPositions(int min, int max)
|
|
{
|
|
Q_D(LtsRangeSlider);
|
|
const int minPosition = qBound(minimum(), qMin(min, max), maximum());
|
|
const int maxPosition = qBound(minimum(), qMax(min, max), maximum());
|
|
|
|
bool emitMinPosChanged = (minPosition != d->m_minimumPosition);
|
|
bool emitMaxPosChanged = (maxPosition != d->m_maximumPosition);
|
|
|
|
if (!emitMinPosChanged && !emitMaxPosChanged)
|
|
return;
|
|
|
|
d->m_minimumPosition = minPosition;
|
|
d->m_maximumPosition = maxPosition;
|
|
|
|
if (!hasTracking())
|
|
update();
|
|
|
|
if (isSliderDown())
|
|
{
|
|
if (emitMinPosChanged)
|
|
emit minimumPositionChanged(d->m_minimumPosition);
|
|
if (emitMaxPosChanged)
|
|
emit maximumPositionChanged(d->m_maximumPosition);
|
|
if (emitMinPosChanged || emitMaxPosChanged)
|
|
emit positionsChanged(d->m_minimumPosition, d->m_maximumPosition);
|
|
}
|
|
if (hasTracking())
|
|
{
|
|
triggerAction(SliderMove);
|
|
setValues(d->m_minimumPosition, d->m_maximumPosition);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
void LtsRangeSlider::setSymmetricMoves(bool symmetry)
|
|
{
|
|
Q_D(LtsRangeSlider);
|
|
d->m_symmetricMoves = symmetry;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
bool LtsRangeSlider::symmetricMoves() const
|
|
{
|
|
Q_D(const LtsRangeSlider);
|
|
return d->m_symmetricMoves;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
void LtsRangeSlider::onRangeChanged(int minimum, int maximum)
|
|
{
|
|
Q_UNUSED(minimum);
|
|
Q_UNUSED(maximum);
|
|
Q_D(LtsRangeSlider);
|
|
setValues(d->m_minimumValue, d->m_maximumValue);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Render
|
|
void LtsRangeSlider::paintEvent(QPaintEvent *)
|
|
{
|
|
Q_D(LtsRangeSlider);
|
|
QStyleOptionSlider option;
|
|
initStyleOption(&option);
|
|
|
|
QStylePainter painter(this);
|
|
|
|
// -------------------------------------------------------------------------
|
|
// Render complete range
|
|
//
|
|
painter.setPen(Qt::lightGray);
|
|
if (option.orientation == Qt::Horizontal)
|
|
{
|
|
painter.drawLine(option.rect.left(), option.rect.top(), option.rect.left(), option.rect.bottom());
|
|
painter.drawRect(option.rect.left(), option.rect.center().y() - 1, option.rect.right() - option.rect.left(), 3);
|
|
painter.drawLine(option.rect.right(), option.rect.top(), option.rect.right(), option.rect.bottom());
|
|
}
|
|
else
|
|
{
|
|
painter.drawLine(option.rect.left(), option.rect.top(), option.rect.right(), option.rect.top());
|
|
painter.drawRect(option.rect.center().x() - 1, option.rect.top(), 3, option.rect.top() - option.rect.bottom());
|
|
painter.drawLine(option.rect.center().x(), option.rect.top(), option.rect.center().x(), option.rect.bottom());
|
|
painter.drawLine(option.rect.left(), option.rect.bottom(), option.rect.right(), option.rect.bottom());
|
|
}
|
|
// -------------------------------------------------------------------------
|
|
|
|
option.subControls = QStyle::SC_SliderGroove;
|
|
// Move to minimum to not highlight the SliderGroove.
|
|
// On mac style, drawing just the slider groove also draws the handles,
|
|
// therefore we give a negative (outside of view) position.
|
|
option.sliderValue = minimum() - maximum();
|
|
option.sliderPosition = minimum() - maximum();
|
|
option.subControls &= ~QStyle::SC_SliderGroove;
|
|
painter.drawComplexControl(QStyle::CC_Slider, option);
|
|
option.subControls |= QStyle::SC_SliderGroove;
|
|
|
|
option.sliderPosition = d->m_minimumPosition;
|
|
const QRect lr = style()->subControlRect(QStyle::CC_Slider,
|
|
&option,
|
|
QStyle::SC_SliderHandle,
|
|
this);
|
|
|
|
option.sliderPosition = d->m_maximumPosition;
|
|
const QRect ur = style()->subControlRect(QStyle::CC_Slider,
|
|
&option,
|
|
QStyle::SC_SliderHandle,
|
|
this);
|
|
|
|
QRect sr = style()->subControlRect(QStyle::CC_Slider,
|
|
&option,
|
|
QStyle::SC_SliderGroove,
|
|
this);
|
|
QRect rangeBox;
|
|
if (option.orientation == Qt::Horizontal)
|
|
{
|
|
rangeBox = QRect(QPoint(qMin(lr.center().x(), ur.center().x()), sr.center().y() - 2),
|
|
QPoint(qMax(lr.center().x(), ur.center().x()), sr.center().y() + 1));
|
|
}
|
|
else
|
|
{
|
|
rangeBox = QRect(QPoint(sr.center().x() - 2, qMin(lr.center().y(), ur.center().y())),
|
|
QPoint(sr.center().x() + 1, qMax(lr.center().y(), ur.center().y())));
|
|
}
|
|
|
|
// -----------------------------
|
|
// Render the range
|
|
//
|
|
QRect groove = style()->subControlRect(QStyle::CC_Slider,
|
|
&option,
|
|
QStyle::SC_SliderGroove,
|
|
this);
|
|
groove.adjust(0, 0, -1, 0);
|
|
|
|
// Create default colors based on the transfer function.
|
|
//
|
|
QColor highlight = palette().color(QPalette::Normal, QPalette::Highlight);
|
|
QLinearGradient gradient;
|
|
if (option.orientation == Qt::Horizontal)
|
|
gradient = QLinearGradient(groove.center().x(), groove.top(), groove.center().x(), groove.bottom());
|
|
else
|
|
gradient = QLinearGradient(groove.left(), groove.center().y(), groove.right(), groove.center().y());
|
|
|
|
// TODO: Set this based on the supplied transfer function
|
|
//QColor l = Qt::darkGray;
|
|
//QColor u = Qt::black;
|
|
|
|
gradient.setColorAt(0, highlight.darker(120));
|
|
gradient.setColorAt(1, highlight.lighter(160));
|
|
|
|
painter.setPen(QPen(highlight.darker(150), 0));
|
|
painter.setBrush(gradient);
|
|
painter.drawRect(rangeBox.intersected(groove));
|
|
|
|
// -----------------------------------
|
|
// Render the sliders
|
|
//
|
|
if (isMinimumSliderDown())
|
|
{
|
|
painter.setClipRect(ur);
|
|
d->drawMaximumSlider(&painter);
|
|
painter.setClipRect(lr);
|
|
d->drawMinimumSlider(&painter);
|
|
}
|
|
else
|
|
{
|
|
painter.setClipRect(lr);
|
|
d->drawMinimumSlider(&painter);
|
|
painter.setClipRect(ur);
|
|
d->drawMaximumSlider(&painter);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Standard Qt UI events
|
|
void LtsRangeSlider::mousePressEvent(QMouseEvent *ev)
|
|
{
|
|
Q_D(LtsRangeSlider);
|
|
if (minimum() == maximum() || (ev->buttons() ^ ev->button()))
|
|
{
|
|
ev->ignore();
|
|
return;
|
|
}
|
|
int mepos = orientation() == Qt::Horizontal ? ev->pos().x() : ev->pos().y();
|
|
|
|
QStyleOptionSlider option;
|
|
initStyleOption(&option);
|
|
|
|
QRect handleRect;
|
|
LtsRangeSliderPrivate::Handle handle_ = d->handleAtPos(ev->pos(), handleRect);
|
|
|
|
if (handle_ != LtsRangeSliderPrivate::NoHandle)
|
|
{
|
|
if (ev->buttons() == Qt::RightButton)
|
|
{
|
|
d->m_subclassPosition = (d->m_minimumPosition + d->m_maximumPosition) / 2.;
|
|
d->m_subclassClickOffset = mepos - d->pixelPosFromRangeValue(d->m_subclassPosition);
|
|
d->m_subclassWidth = (d->m_maximumPosition - d->m_minimumPosition) / 2.;
|
|
//qMax(d->m_subclassPosition - d->m_minimumPosition, d->m_maximumPosition - d->m_subclassPosition);
|
|
setSliderDown(true);
|
|
if (!isMinimumSliderDown() || !isMaximumSliderDown())
|
|
{
|
|
d->m_selectedHandles = QFlags<LtsRangeSliderPrivate::Handle>(LtsRangeSliderPrivate::MinimumHandle) |
|
|
QFlags<LtsRangeSliderPrivate::Handle>(LtsRangeSliderPrivate::MaximumHandle);
|
|
QRect sr = style()->subControlRect(QStyle::CC_Slider, &option, QStyle::SC_SliderGroove, this);
|
|
update(handleRect.united(sr));
|
|
}
|
|
ev->accept();
|
|
return;
|
|
}
|
|
|
|
d->m_subclassPosition = (handle_ == LtsRangeSliderPrivate::MinimumHandle) ? d->m_minimumPosition : d->m_maximumPosition;
|
|
|
|
// save the position of the mouse inside the handle for later
|
|
d->m_subclassClickOffset = mepos - (orientation() == Qt::Horizontal ? handleRect.left() : handleRect.top());
|
|
|
|
setSliderDown(true);
|
|
|
|
if (d->m_selectedHandles != handle_)
|
|
{
|
|
d->m_selectedHandles = handle_;
|
|
update(handleRect);
|
|
}
|
|
// Accept the mouseEvent
|
|
ev->accept();
|
|
return;
|
|
}
|
|
|
|
// if we are here, no handles have been pressed
|
|
// Check if we pressed on the groove between the 2 handles
|
|
|
|
QStyle::SubControl control = style()->hitTestComplexControl(QStyle::CC_Slider, &option, ev->pos(), this);
|
|
QRect sr = style()->subControlRect(QStyle::CC_Slider, &option, QStyle::SC_SliderGroove, this);
|
|
int minCenter = (orientation() == Qt::Horizontal ? handleRect.left() : handleRect.top());
|
|
int maxCenter = (orientation() == Qt::Horizontal ? handleRect.right() : handleRect.bottom());
|
|
if (control == QStyle::SC_SliderGroove && mepos > minCenter && mepos < maxCenter)
|
|
{
|
|
// warning lost of precision it might be fatal
|
|
d->m_subclassPosition = (d->m_minimumPosition + d->m_maximumPosition) / 2.;
|
|
d->m_subclassClickOffset = mepos - d->pixelPosFromRangeValue(d->m_subclassPosition);
|
|
d->m_subclassWidth = (d->m_maximumPosition - d->m_minimumPosition) / 2.;
|
|
//qMax(d->m_subclassPosition - d->m_minimumPosition, d->m_maximumPosition - d->m_subclassPosition);
|
|
setSliderDown(true);
|
|
if (!isMinimumSliderDown() || !isMaximumSliderDown())
|
|
{
|
|
d->m_selectedHandles = QFlags<LtsRangeSliderPrivate::Handle>(LtsRangeSliderPrivate::MinimumHandle) |
|
|
QFlags<LtsRangeSliderPrivate::Handle>(LtsRangeSliderPrivate::MaximumHandle);
|
|
update(handleRect.united(sr));
|
|
}
|
|
ev->accept();
|
|
return;
|
|
}
|
|
ev->ignore();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Standard Qt UI events
|
|
void LtsRangeSlider::mouseMoveEvent(QMouseEvent *ev)
|
|
{
|
|
Q_D(LtsRangeSlider);
|
|
if (!d->m_selectedHandles)
|
|
{
|
|
ev->ignore();
|
|
return;
|
|
}
|
|
int mepos = orientation() == Qt::Horizontal ? ev->pos().x() : ev->pos().y();
|
|
|
|
QStyleOptionSlider option;
|
|
initStyleOption(&option);
|
|
|
|
// Both handles are down (the user clicked in between the handles)
|
|
if (isMinimumSliderDown() && isMaximumSliderDown())
|
|
{
|
|
int newPosition = d->pixelPosToRangeValue(mepos);
|
|
int newMinPosition = newPosition - static_cast<int>(d->m_subclassWidth);
|
|
int newMaxPosition = newPosition + static_cast<int>(d->m_subclassWidth + .5);
|
|
// prevent the range from becoming smaller
|
|
if (newMinPosition < minimum())
|
|
{
|
|
newMinPosition = minimum();
|
|
newMaxPosition = newMinPosition + (d->m_maximumPosition - d->m_minimumPosition);
|
|
}
|
|
else if (newMaxPosition > maximum())
|
|
{
|
|
newMaxPosition = maximum();
|
|
newMinPosition = newMaxPosition - (d->m_maximumPosition - d->m_minimumPosition);
|
|
}
|
|
setPositions(newMinPosition, newMaxPosition);
|
|
ev->accept();
|
|
return;
|
|
}
|
|
|
|
const int m = style()->pixelMetric(QStyle::PM_MaximumDragDistance, &option, this);
|
|
|
|
int newPosition = d->pixelPosToRangeValue(mepos - d->m_subclassClickOffset);
|
|
|
|
if (m >= 0)
|
|
{
|
|
const QRect r = rect().adjusted(-m, -m, m, m);
|
|
if (!r.contains(ev->pos()))
|
|
newPosition = d->m_subclassPosition;
|
|
}
|
|
|
|
// Only the lower/left slider is down
|
|
if (isMinimumSliderDown() && !isMaximumSliderDown())
|
|
{
|
|
double newMinPos = qMin(newPosition, d->m_maximumPosition);
|
|
setPositions(newMinPos, d->m_maximumPosition + (d->m_symmetricMoves ? d->m_minimumPosition - newMinPos : 0));
|
|
}
|
|
// Only the upper/right slider is down
|
|
else if (isMaximumSliderDown() && !isMinimumSliderDown())
|
|
{
|
|
double newMaxPos = qMax(d->m_minimumPosition, newPosition);
|
|
setPositions(d->m_minimumPosition - (d->m_symmetricMoves ? newMaxPos - d->m_maximumPosition : 0), newMaxPos);
|
|
}
|
|
ev->accept();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Standard Qt UI mouseEvents
|
|
void LtsRangeSlider::mouseReleaseEvent(QMouseEvent *ev)
|
|
{
|
|
Q_D(LtsRangeSlider);
|
|
QSlider::mouseReleaseEvent(ev);
|
|
|
|
setSliderDown(false);
|
|
d->m_selectedHandles = {};
|
|
|
|
update();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
bool LtsRangeSlider::isMinimumSliderDown() const
|
|
{
|
|
Q_D(const LtsRangeSlider);
|
|
return d->m_selectedHandles & LtsRangeSliderPrivate::MinimumHandle;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
bool LtsRangeSlider::isMaximumSliderDown() const
|
|
{
|
|
Q_D(const LtsRangeSlider);
|
|
return d->m_selectedHandles & LtsRangeSliderPrivate::MaximumHandle;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
void LtsRangeSlider::initMinimumSliderStyleOption(QStyleOptionSlider *option) const
|
|
{
|
|
initStyleOption(option);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
void LtsRangeSlider::initMaximumSliderStyleOption(QStyleOptionSlider *option) const
|
|
{
|
|
initStyleOption(option);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
QString LtsRangeSlider::handleToolTip()const
|
|
{
|
|
Q_D(const LtsRangeSlider);
|
|
return d->m_handleToolTip;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
void LtsRangeSlider::setHandleToolTip(const QString &toolTip)
|
|
{
|
|
Q_D(LtsRangeSlider);
|
|
d->m_handleToolTip = toolTip;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
bool LtsRangeSlider::event(QEvent *event)
|
|
{
|
|
Q_D(LtsRangeSlider);
|
|
switch (event->type())
|
|
{
|
|
case QEvent::ToolTip:
|
|
{
|
|
QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
|
|
QStyleOptionSlider opt;
|
|
// Test the MinimumHandle
|
|
opt.sliderPosition = d->m_minimumPosition;
|
|
opt.sliderValue = d->m_minimumValue;
|
|
initStyleOption(&opt);
|
|
QStyle::SubControl hoveredControl = style()->hitTestComplexControl(QStyle::CC_Slider, &opt, helpEvent->pos(), this);
|
|
if (!d->m_handleToolTip.isEmpty() && hoveredControl == QStyle::SC_SliderHandle)
|
|
{
|
|
QToolTip::showText(helpEvent->globalPos(), d->m_handleToolTip.arg(minimumValue()));
|
|
event->accept();
|
|
return true;
|
|
}
|
|
// Test the MaximumHandle
|
|
opt.sliderPosition = d->m_maximumPosition;
|
|
opt.sliderValue = d->m_maximumValue;
|
|
initStyleOption(&opt);
|
|
hoveredControl = style()->hitTestComplexControl(QStyle::CC_Slider, &opt, helpEvent->pos(), this);
|
|
if (!d->m_handleToolTip.isEmpty() && hoveredControl == QStyle::SC_SliderHandle)
|
|
{
|
|
QToolTip::showText(helpEvent->globalPos(), d->m_handleToolTip.arg(maximumValue()));
|
|
event->accept();
|
|
return true;
|
|
}
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return QSlider::event(event);
|
|
}
|
|
|
|
} // namespace
|