#include "FractoriumPch.h"
#include "DoubleSpinBox.h"
QTimer DoubleSpinBox::s_Timer;
///
/// Constructor that passes parent to the base and sets up height and step.
/// Specific focus policy is used to allow the user to hover over the control
/// and change its value using the mouse wheel without explicitly having to click
/// inside of it.
///
/// The parent widget. Default: nullptr.
/// The height of the spin box. Default: 16.
/// The step used to increment/decrement the spin box when using the mouse wheel. Default: 0.05.
DoubleSpinBox::DoubleSpinBox(QWidget* p, int h, double step)
: QDoubleSpinBox(p)
{
m_Select = false;
m_DoubleClick = false;
m_DoubleClickNonZero = 0;
m_DoubleClickZero = 1;
m_Step = step;
m_SmallStep = step / 10.0;
setSingleStep(step);
setFrame(false);
setButtonSymbols(QAbstractSpinBox::NoButtons);
setFocusPolicy(Qt::StrongFocus);
setMinimumHeight(h);//setGeometry() has no effect, so must set both of these instead.
setMaximumHeight(h);
setContextMenuPolicy(Qt::PreventContextMenu);
lineEdit()->installEventFilter(this);
lineEdit()->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
connect(this, SIGNAL(valueChanged(double)), this, SLOT(OnSpinBoxValueChanged(double)), Qt::QueuedConnection);
}
///
/// Set the value of the control without triggering signals.
///
/// The value to set it to
void DoubleSpinBox::SetValueStealth(double d)
{
blockSignals(true);
setValue(d);
blockSignals(false);
}
///
/// Set whether to respond to double click events.
///
/// True if this should respond to double click events, else false.
void DoubleSpinBox::DoubleClick(bool b)
{
m_DoubleClick = b;
}
///
/// Set the value to be used when the user double clicks the spinner while
/// it contains zero.
///
/// The value to be used
void DoubleSpinBox::DoubleClickZero(double val)
{
m_DoubleClickZero = val;
}
///
/// Set the value to be used when the user double clicks the spinner while
/// it contains a non-zero value.
///
/// The value to be used
void DoubleSpinBox::DoubleClickNonZero(double val)
{
m_DoubleClickNonZero = val;
}
///
/// Get the default step used when the user scrolls.
///
double DoubleSpinBox::Step()
{
return m_Step;
}
///
/// Set the default step to be used when the user scrolls.
///
/// The step to use for scrolling
void DoubleSpinBox::Step(double step)
{
m_Step = step;
}
///
/// Get the small step to be used when the user holds down shift while scrolling.
///
double DoubleSpinBox::SmallStep()
{
return m_SmallStep;
}
///
/// Set the small step to be used when the user holds down shift while scrolling.
/// The default is step / 10, so use this if something else is needed.
///
/// The small step to use for scrolling while the shift key is down
void DoubleSpinBox::SmallStep(double step)
{
m_SmallStep = step;
}
///
/// Expose the underlying QLineEdit control to the caller.
///
/// A pointer to the QLineEdit
QLineEdit* DoubleSpinBox::lineEdit()
{
return QDoubleSpinBox::lineEdit();
}
///
/// Another workaround for the persistent text selection bug in Qt.
///
void DoubleSpinBox::OnSpinBoxValueChanged(double d)
{
lineEdit()->deselect();//Gets rid of nasty "feature" that always has text selected.
}
///
/// Called while the timer is activated due to the right mouse button being held down.
///
void DoubleSpinBox::OnTimeout()
{
int xdistance = m_MouseMovePoint.x() - m_MouseDownPoint.x();
int ydistance = m_MouseMovePoint.y() - m_MouseDownPoint.y();
int distance = abs(xdistance) > abs(ydistance) ? xdistance : ydistance;
double scale, val;
double d = value();
bool shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
bool ctrl = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier);
double amount = (m_SmallStep + m_Step) * 0.5;
if (shift)
scale = 0.0001;
else if (ctrl)
scale = 0.01;
else
scale = 0.001;
val = d + (distance * amount * scale);
setValue(val);
}
///
/// Event filter for taking special action on double click events.
///
/// The object
/// The eevent
/// false
bool DoubleSpinBox::eventFilter(QObject* o, QEvent* e)
{
QMouseEvent* me = dynamic_cast(e);
if (isEnabled() &&
me &&
me->type() == QMouseEvent::MouseButtonPress &&
me->button() == Qt::RightButton)
{
m_MouseDownPoint = m_MouseMovePoint = me->pos();
StartTimer();
}
else if (isEnabled() &&
me &&
me->type() == QMouseEvent::MouseButtonRelease &&
me->button() == Qt::RightButton)
{
StopTimer();
m_MouseDownPoint = m_MouseMovePoint = me->pos();
}
else if (isEnabled() &&
me &&
me->type() == QMouseEvent::MouseMove &&
QGuiApplication::mouseButtons() & Qt::RightButton)
{
m_MouseMovePoint = me->pos();
}
else if (m_DoubleClick && e->type() == QMouseEvent::MouseButtonDblClick && isEnabled())
{
if (IsNearZero(value()))
setValue(m_DoubleClickZero);
else
setValue(m_DoubleClickNonZero);
}
else
{
if (e->type() == QEvent::Wheel)
{
//Take special action for shift to reduce the scroll amount. Control already
//increases it automatically.
if (QWheelEvent* we = dynamic_cast(e))
{
Qt::KeyboardModifiers mod = we->modifiers();
if (mod.testFlag(Qt::ShiftModifier))
setSingleStep(m_SmallStep);
else
setSingleStep(m_Step);
}
}
}
return QDoubleSpinBox::eventFilter(o, e);
}
///
/// Called when focus enters the spinner.
///
/// The event
void DoubleSpinBox::focusInEvent(QFocusEvent* e)
{
StopTimer();
QDoubleSpinBox::focusInEvent(e);
}
///
/// Called when focus leaves the spinner.
/// Qt has a nasty "feature" that leaves the text in a spinner selected
/// and the cursor visible, regardless of whether it has the focus.
/// Manually clear both here.
///
/// The event
void DoubleSpinBox::focusOutEvent(QFocusEvent* e)
{
StopTimer();
QDoubleSpinBox::focusOutEvent(e);
}
///
/// Called when focus enters the spinner.
/// Must set the focus to make sure key down messages don't erroneously go to the GLWidget.
///
/// The event
void DoubleSpinBox::enterEvent(QEvent* e)
{
StopTimer();
QDoubleSpinBox::enterEvent(e);
}
///
/// Called when focus leaves the spinner.
/// Must clear the focus to make sure key down messages don't erroneously go to the GLWidget.
///
/// The event
void DoubleSpinBox::leaveEvent(QEvent* e)
{
StopTimer();
QDoubleSpinBox::leaveEvent(e);
}
///
/// Start the timer in response to the right mouse button being pressed.
///
void DoubleSpinBox::StartTimer()
{
s_Timer.stop();
connect(&s_Timer, SIGNAL(timeout()), this, SLOT(OnTimeout()));
s_Timer.start(300);
}
///
/// Stop the timer in response to the left mouse button being pressed.
///
void DoubleSpinBox::StopTimer()
{
s_Timer.stop();
disconnect(&s_Timer, SIGNAL(timeout()), this, SLOT(OnTimeout()));
}