Files
qt6-migration-pipeline/Lts/Common/LtsWidgets/lts_range_slider.cpp
T

824 lines
22 KiB
C++
Executable File

#include "lts_range_slider.h"
#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:
enum Handle
{
NoHandle = 0x0,
MinimumHandle = 0x1,
MaximumHandle = 0x2
};
Q_DECLARE_FLAGS(Handles, Handle)
explicit LtsRangeSliderPrivate(LtsRangeSlider &object);
void init();
Handle handleAtPos(const QPoint &pos, QRect &handleRect) const;
int pixelPosToRangeValue(int pos) const;
int pixelPosFromRangeValue(int val) const;
void drawMinimumSlider(QStylePainter *painter) const;
void drawMaximumSlider(QStylePainter *painter) const;
int m_minimumValue = 0;
int m_maximumValue = 100;
int m_minimumPosition = 0;
int m_maximumPosition = 100;
QStyle::SubControl m_minimumSliderSelected = QStyle::SC_None;
QStyle::SubControl m_maximumSliderSelected = QStyle::SC_None;
int m_subclassClickOffset = 0;
int m_subclassPosition = 0;
float m_subclassWidth = 0;
LtsRangeSliderPrivate::Handles m_selectedHandles = {};
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);
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);
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);
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
{
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;
}
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;
}
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
QRect clip = q->style()->subControlRect(QStyle::CC_Slider, &option,
QStyle::SC_SliderHandle, q);
painter->setClipRect(clip);
#endif
painter->drawComplexControl(QStyle::CC_Slider, option);
}
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
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);
}
void LtsRangeSlider::paintEvent(QPaintEvent *)
{
Q_D(LtsRangeSlider);
QStyleOptionSlider option;
initStyleOption(&option);
QStylePainter painter(this);
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;
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())));
}
QRect groove = style()->subControlRect(QStyle::CC_Slider,
&option,
QStyle::SC_SliderGroove,
this);
groove.adjust(0, 0, -1, 0);
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());
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));
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);
}
}
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.;
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;
d->m_subclassClickOffset = mepos - (orientation() == Qt::Horizontal ? handleRect.left() : handleRect.top());
setSliderDown(true);
if (d->m_selectedHandles != handle_)
{
d->m_selectedHandles = handle_;
update(handleRect);
}
ev->accept();
return;
}
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)
{
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.;
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();
}
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);
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);
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;
}
if (isMinimumSliderDown() && !isMaximumSliderDown())
{
double newMinPos = qMin(newPosition, d->m_maximumPosition);
setPositions(newMinPos, d->m_maximumPosition + (d->m_symmetricMoves ? d->m_minimumPosition - newMinPos : 0));
}
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();
}
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;
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;
}
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);
}
}