By default, QSlider moves its handle by a value belonging to the pageStep()
property on mouse click. To make the handle jump directly at the mouse click point, we need to create a new class inherited by QSlider.
.h
#ifndef QIMPROVEDSLIDER_H
#define QIMPROVEDSLIDER_H
#include <QSlider>
class QImprovedSlider : public QSlider
{
Q_OBJECT
protected:
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
public:
explicit QImprovedSlider(QWidget *parent = 0);
signals:
void clicked(int value) const;
};
#endif // QIMPROVEDSLIDER_H
.cpp
#include <QWidget>
#include <QMouseEvent>
#include <QStyle>
#include <QStyleOptionSlider>
#include <QProxyStyle>
#include "QImprovedSlider.h"
class QImprovedSliderStyle : public QProxyStyle
{
public:
using QProxyStyle::QProxyStyle;
int styleHint(QStyle::StyleHint hint, const QStyleOption* option = 0,
const QWidget* widget = 0, QStyleHintReturn* returnData = 0) const
{
if (hint == QStyle::SH_Slider_AbsoluteSetButtons)
return (Qt::LeftButton | Qt::MidButton | Qt::RightButton);
return QProxyStyle::styleHint(hint, option, widget, returnData);
}
};
QImprovedSlider::QImprovedSlider(QWidget *parent) :
QSlider(parent)
{
setStyle(new QImprovedSliderStyle(this->style()));
}
void QImprovedSlider::mousePressEvent(QMouseEvent *event) {
QStyleOptionSlider opt;
initStyleOption(&opt);
QRect sr = style()->subControlRect(QStyle::CC_Slider,
&opt,
QStyle::SC_SliderHandle,
this);
qDebug() << sr.height() << sr.width();
if (!sr.contains(event->pos()) && event->button() == Qt::LeftButton) {
if (orientation() == Qt::Vertical)
setValue(minimum() + ((maximum()-minimum()) * (height()-event->y())) / height() ) ;
else
setValue(minimum() + ((maximum()-minimum()) * event->x()) / width() ) ;
}
QSlider::mousePressEvent(event);
}
void QImprovedSlider::mouseReleaseEvent(QMouseEvent *event) {
if (event->button() == Qt::LeftButton) {
emit clicked(value());
QSlider::mouseReleaseEvent(event);
}
}
QImprovedSliderStyle make the handle drag more fluid, but in this way, the event is fired even when the click falls inside the handle, while the condition !sr.contains(event->pos())
should avoid this.