Files
OmaPhoto/src/UI/NumericScrub.cpp
T

84 lines
2.7 KiB
C++

#include "UI/NumericScrub.h"
#include <QApplication>
#include <QMouseEvent>
#include <QWidget>
#include <algorithm>
#include <cmath>
NumericScrub::NumericScrub(QWidget *label, Options options) : QObject(label), m_label(label), m_options(std::move(options))
{
// A disabled label shows its parent's cursor, as Qt does.
label->setCursor(Qt::SizeHorCursor);
// Buttonless moves must arrive to end a lost release.
label->setMouseTracking(true);
label->installEventFilter(this);
}
bool NumericScrub::eventFilter(QObject *watched, QEvent *event)
{
const QEvent::Type type = event->type();
// A double click's second press is a press too.
const bool press = type == QEvent::MouseButtonPress || type == QEvent::MouseButtonDblClick;
if (!press && type != QEvent::MouseMove && type != QEvent::MouseButtonRelease)
return false;
const auto *mouse = static_cast<QMouseEvent *>(event);
if (watched != m_label) {
// A popup opened mid-drag takes the release.
if (type == QEvent::MouseButtonRelease && mouse->button() == Qt::LeftButton)
end();
return false;
}
if (press) {
// Filters see a disabled label's presses before Qt drops them.
if (mouse->button() != Qt::LeftButton || !m_label->isEnabled())
return false;
// The label holds the grab: its own points stay exact.
m_press = mouse->position();
qApp->installEventFilter(this);
return true;
}
if (!m_press)
return false;
if (type == QEvent::MouseButtonRelease) {
if (mouse->button() == Qt::LeftButton)
end();
return true;
}
// Qt can lose a release: a buttonless move ends.
if (!mouse->buttons().testFlag(Qt::LeftButton)) {
end();
return true;
}
const QPointF translation = mouse->position() - *m_press;
if (!m_start) {
if (std::hypot(translation.x(), translation.y()) < 1)
return true;
m_start = m_options.value();
m_options.onStart();
}
double proposed = *m_start + translation.x() * m_options.sensitivity;
if (m_options.step && *m_options.step > 0)
proposed = std::round(proposed / *m_options.step) * *m_options.step;
m_options.set(std::min(m_options.high, std::max(m_options.low, proposed)));
return true;
}
void NumericScrub::reshape(double sensitivity, double low, double high)
{
m_options.sensitivity = sensitivity;
m_options.low = low;
m_options.high = high;
}
void NumericScrub::end()
{
if (!m_press)
return;
qApp->removeEventFilter(this);
m_press.reset();
if (m_start) {
m_start.reset();
m_options.onEnd();
}
}