Apply Qt::ControlModifier to all spin box interactions
A step modifier already existed when scrolling with the Qt::ControlModifier held. This patch applies this functionality to other methods of stepping a spin box. Holding the modifier increases the step rate when: - scrolling; - pressing the up/down keys; - pressing the spin box up/down buttons. [ChangeLog][QtWidgets][QAbstractSpinBox] The Qt::ControlModifier increases the number of steps a QAbstractSpinBox takes for the following interactions: scrolling, up/down keyboard keys and the spin box buttons. Previously, Qt::ControlModifier only affected scrolling. Task-number: QTBUG-67380 Change-Id: Icc8754d5c007da0771bfaef113603a2f334dd494 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
This commit is contained in:
parent
cb3e85fc9a
commit
2d1e1f569b
@ -100,6 +100,12 @@ QT_BEGIN_NAMESPACE
|
|||||||
integer value to signify how many steps were taken. E.g. Pressing
|
integer value to signify how many steps were taken. E.g. Pressing
|
||||||
Qt::Key_Down will trigger a call to stepBy(-1).
|
Qt::Key_Down will trigger a call to stepBy(-1).
|
||||||
|
|
||||||
|
When the user triggers a step whilst holding the Qt::ControlModifier,
|
||||||
|
QAbstractSpinBox steps by 10 instead of making a single step. This
|
||||||
|
step modifier affects wheel events, key events and interaction with
|
||||||
|
the spinbox buttons. Note that on macOS, Control corresponds to the
|
||||||
|
Command key.
|
||||||
|
|
||||||
QAbstractSpinBox also provide a virtual function stepEnabled() to
|
QAbstractSpinBox also provide a virtual function stepEnabled() to
|
||||||
determine whether stepping up/down is allowed at any point. This
|
determine whether stepping up/down is allowed at any point. This
|
||||||
function returns a bitset of StepEnabled.
|
function returns a bitset of StepEnabled.
|
||||||
@ -1015,6 +1021,8 @@ void QAbstractSpinBox::keyPressEvent(QKeyEvent *event)
|
|||||||
const bool up = (event->key() == Qt::Key_PageUp || event->key() == Qt::Key_Up);
|
const bool up = (event->key() == Qt::Key_PageUp || event->key() == Qt::Key_Up);
|
||||||
if (!(stepEnabled() & (up ? StepUpEnabled : StepDownEnabled)))
|
if (!(stepEnabled() & (up ? StepUpEnabled : StepDownEnabled)))
|
||||||
return;
|
return;
|
||||||
|
if (!isPgUpOrDown && (event->modifiers() & d->stepModifier))
|
||||||
|
steps *= 10;
|
||||||
if (!up)
|
if (!up)
|
||||||
steps *= -1;
|
steps *= -1;
|
||||||
if (style()->styleHint(QStyle::SH_SpinBox_AnimateButton, 0, this)) {
|
if (style()->styleHint(QStyle::SH_SpinBox_AnimateButton, 0, this)) {
|
||||||
@ -1141,11 +1149,24 @@ void QAbstractSpinBox::keyReleaseEvent(QKeyEvent *event)
|
|||||||
void QAbstractSpinBox::wheelEvent(QWheelEvent *event)
|
void QAbstractSpinBox::wheelEvent(QWheelEvent *event)
|
||||||
{
|
{
|
||||||
Q_D(QAbstractSpinBox);
|
Q_D(QAbstractSpinBox);
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
// If the event comes from a real mouse wheel, rather than a track pad
|
||||||
|
// (Qt::MouseEventSynthesizedBySystem), the shift modifier changes the
|
||||||
|
// scroll orientation to horizontal.
|
||||||
|
// Convert horizontal events back to vertical whilst shift is held.
|
||||||
|
if ((event->modifiers() & Qt::ShiftModifier)
|
||||||
|
&& event->source() == Qt::MouseEventNotSynthesized) {
|
||||||
|
d->wheelDeltaRemainder += event->angleDelta().x();
|
||||||
|
} else {
|
||||||
|
d->wheelDeltaRemainder += event->angleDelta().y();
|
||||||
|
}
|
||||||
|
#else
|
||||||
d->wheelDeltaRemainder += event->angleDelta().y();
|
d->wheelDeltaRemainder += event->angleDelta().y();
|
||||||
|
#endif
|
||||||
const int steps = d->wheelDeltaRemainder / 120;
|
const int steps = d->wheelDeltaRemainder / 120;
|
||||||
d->wheelDeltaRemainder -= steps * 120;
|
d->wheelDeltaRemainder -= steps * 120;
|
||||||
if (stepEnabled() & (steps > 0 ? StepUpEnabled : StepDownEnabled))
|
if (stepEnabled() & (steps > 0 ? StepUpEnabled : StepDownEnabled))
|
||||||
stepBy(event->modifiers() & Qt::ControlModifier ? steps * 10 : steps);
|
stepBy(event->modifiers() & d->stepModifier ? steps * 10 : steps);
|
||||||
event->accept();
|
event->accept();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -1245,18 +1266,19 @@ void QAbstractSpinBox::timerEvent(QTimerEvent *event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (doStep) {
|
if (doStep) {
|
||||||
|
const bool increaseStepRate = QGuiApplication::keyboardModifiers() & d->stepModifier;
|
||||||
const StepEnabled st = stepEnabled();
|
const StepEnabled st = stepEnabled();
|
||||||
if (d->buttonState & Up) {
|
if (d->buttonState & Up) {
|
||||||
if (!(st & StepUpEnabled)) {
|
if (!(st & StepUpEnabled)) {
|
||||||
d->reset();
|
d->reset();
|
||||||
} else {
|
} else {
|
||||||
stepBy(1);
|
stepBy(increaseStepRate ? 10 : 1);
|
||||||
}
|
}
|
||||||
} else if (d->buttonState & Down) {
|
} else if (d->buttonState & Down) {
|
||||||
if (!(st & StepDownEnabled)) {
|
if (!(st & StepDownEnabled)) {
|
||||||
d->reset();
|
d->reset();
|
||||||
} else {
|
} else {
|
||||||
stepBy(-1);
|
stepBy(increaseStepRate ? -10 : -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -1384,8 +1406,9 @@ QAbstractSpinBoxPrivate::QAbstractSpinBoxPrivate()
|
|||||||
cachedState(QValidator::Invalid), pendingEmit(false), readOnly(false), wrapping(false),
|
cachedState(QValidator::Invalid), pendingEmit(false), readOnly(false), wrapping(false),
|
||||||
ignoreCursorPositionChanged(false), frame(true), accelerate(false), keyboardTracking(true),
|
ignoreCursorPositionChanged(false), frame(true), accelerate(false), keyboardTracking(true),
|
||||||
cleared(false), ignoreUpdateEdit(false), correctionMode(QAbstractSpinBox::CorrectToPreviousValue),
|
cleared(false), ignoreUpdateEdit(false), correctionMode(QAbstractSpinBox::CorrectToPreviousValue),
|
||||||
acceleration(0), hoverControl(QStyle::SC_None), buttonSymbols(QAbstractSpinBox::UpDownArrows), validator(0),
|
stepModifier(Qt::ControlModifier), acceleration(0), hoverControl(QStyle::SC_None),
|
||||||
showGroupSeparator(0), wheelDeltaRemainder(0)
|
buttonSymbols(QAbstractSpinBox::UpDownArrows), validator(0), showGroupSeparator(0),
|
||||||
|
wheelDeltaRemainder(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1636,7 +1659,10 @@ void QAbstractSpinBoxPrivate::updateState(bool up, bool fromKeyboard /* = false
|
|||||||
: QAbstractSpinBox::StepDownEnabled))) {
|
: QAbstractSpinBox::StepDownEnabled))) {
|
||||||
spinClickThresholdTimerId = q->startTimer(spinClickThresholdTimerInterval);
|
spinClickThresholdTimerId = q->startTimer(spinClickThresholdTimerInterval);
|
||||||
buttonState = (up ? Up : Down) | (fromKeyboard ? Keyboard : Mouse);
|
buttonState = (up ? Up : Down) | (fromKeyboard ? Keyboard : Mouse);
|
||||||
q->stepBy(up ? 1 : -1);
|
int steps = up ? 1 : -1;
|
||||||
|
if (QGuiApplication::keyboardModifiers() & stepModifier)
|
||||||
|
steps *= 10;
|
||||||
|
q->stepBy(steps);
|
||||||
#ifndef QT_NO_ACCESSIBILITY
|
#ifndef QT_NO_ACCESSIBILITY
|
||||||
QAccessibleValueChangeEvent event(q, value);
|
QAccessibleValueChangeEvent event(q, value);
|
||||||
QAccessible::updateAccessibility(&event);
|
QAccessible::updateAccessibility(&event);
|
||||||
|
@ -146,6 +146,7 @@ public:
|
|||||||
uint ignoreUpdateEdit : 1;
|
uint ignoreUpdateEdit : 1;
|
||||||
QAbstractSpinBox::CorrectionMode correctionMode;
|
QAbstractSpinBox::CorrectionMode correctionMode;
|
||||||
QAbstractSpinBox::StepType stepType = QAbstractSpinBox::StepType::DefaultStepType;
|
QAbstractSpinBox::StepType stepType = QAbstractSpinBox::StepType::DefaultStepType;
|
||||||
|
Qt::KeyboardModifier stepModifier = Qt::ControlModifier;
|
||||||
int acceleration;
|
int acceleration;
|
||||||
QStyle::SubControl hoverControl;
|
QStyle::SubControl hoverControl;
|
||||||
QRect hoverRect;
|
QRect hoverRect;
|
||||||
|
@ -70,6 +70,7 @@
|
|||||||
#include <QSignalSpy>
|
#include <QSignalSpy>
|
||||||
#include <QTestEventList>
|
#include <QTestEventList>
|
||||||
#include <QDateEdit>
|
#include <QDateEdit>
|
||||||
|
#include <QProxyStyle>
|
||||||
|
|
||||||
#include <private/qdatetimeedit_p.h>
|
#include <private/qdatetimeedit_p.h>
|
||||||
|
|
||||||
@ -93,6 +94,26 @@ public:
|
|||||||
friend class tst_QDateTimeEdit;
|
friend class tst_QDateTimeEdit;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PressAndHoldStyle : public QProxyStyle
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
using QProxyStyle::QProxyStyle;
|
||||||
|
|
||||||
|
int styleHint(QStyle::StyleHint hint, const QStyleOption *option = nullptr,
|
||||||
|
const QWidget *widget = nullptr, QStyleHintReturn *returnData = nullptr) const override
|
||||||
|
{
|
||||||
|
switch (hint) {
|
||||||
|
case QStyle::SH_SpinBox_ClickAutoRepeatRate:
|
||||||
|
return 5;
|
||||||
|
case QStyle::SH_SpinBox_ClickAutoRepeatThreshold:
|
||||||
|
return 10;
|
||||||
|
default:
|
||||||
|
return QProxyStyle::styleHint(hint, option, widget, returnData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class tst_QDateTimeEdit : public QObject
|
class tst_QDateTimeEdit : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -205,9 +226,8 @@ private slots:
|
|||||||
void reverseTest();
|
void reverseTest();
|
||||||
|
|
||||||
void ddMMMMyyyy();
|
void ddMMMMyyyy();
|
||||||
#if QT_CONFIG(wheelevent)
|
void wheelEvent_data();
|
||||||
void wheelEvent();
|
void wheelEvent();
|
||||||
#endif
|
|
||||||
|
|
||||||
void specialValueCornerCase();
|
void specialValueCornerCase();
|
||||||
void cursorPositionOnInit();
|
void cursorPositionOnInit();
|
||||||
@ -245,14 +265,99 @@ private slots:
|
|||||||
void dateEditCorrectSectionSize_data();
|
void dateEditCorrectSectionSize_data();
|
||||||
void dateEditCorrectSectionSize();
|
void dateEditCorrectSectionSize();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void stepModifierKeys_data();
|
||||||
|
void stepModifierKeys();
|
||||||
|
|
||||||
|
void stepModifierButtons_data();
|
||||||
|
void stepModifierButtons();
|
||||||
|
|
||||||
|
void stepModifierPressAndHold_data();
|
||||||
|
void stepModifierPressAndHold();
|
||||||
private:
|
private:
|
||||||
EditorDateEdit* testWidget;
|
EditorDateEdit* testWidget;
|
||||||
QWidget *testFocusWidget;
|
QWidget *testFocusWidget;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef QList<QDate> DateList;
|
||||||
typedef QList<QTime> TimeList;
|
typedef QList<QTime> TimeList;
|
||||||
typedef QList<Qt::Key> KeyList;
|
typedef QList<Qt::Key> KeyList;
|
||||||
|
|
||||||
|
static QLatin1String modifierToName(Qt::KeyboardModifier modifier)
|
||||||
|
{
|
||||||
|
switch (modifier) {
|
||||||
|
case Qt::NoModifier:
|
||||||
|
return QLatin1Literal("No");
|
||||||
|
break;
|
||||||
|
case Qt::ControlModifier:
|
||||||
|
return QLatin1Literal("Ctrl");
|
||||||
|
break;
|
||||||
|
case Qt::ShiftModifier:
|
||||||
|
return QLatin1Literal("Shift");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qFatal("Unexpected keyboard modifier");
|
||||||
|
return QLatin1String();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static QLatin1String sectionToName(const QDateTimeEdit::Section section)
|
||||||
|
{
|
||||||
|
switch (section) {
|
||||||
|
case QDateTimeEdit::SecondSection:
|
||||||
|
return QLatin1Literal("Second");
|
||||||
|
case QDateTimeEdit::MinuteSection:
|
||||||
|
return QLatin1Literal("Minute");
|
||||||
|
case QDateTimeEdit::HourSection:
|
||||||
|
return QLatin1Literal("Hours");
|
||||||
|
case QDateTimeEdit::DaySection:
|
||||||
|
return QLatin1Literal("Day");
|
||||||
|
case QDateTimeEdit::MonthSection:
|
||||||
|
return QLatin1Literal("Month");
|
||||||
|
case QDateTimeEdit::YearSection:
|
||||||
|
return QLatin1Literal("Year");
|
||||||
|
default:
|
||||||
|
qFatal("Unexpected section");
|
||||||
|
return QLatin1String();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static QDate stepDate(const QDate& startDate, const QDateTimeEdit::Section section,
|
||||||
|
const int steps)
|
||||||
|
{
|
||||||
|
switch (section) {
|
||||||
|
case QDateTimeEdit::DaySection:
|
||||||
|
return startDate.addDays(steps);
|
||||||
|
case QDateTimeEdit::MonthSection:
|
||||||
|
return startDate.addMonths(steps);
|
||||||
|
case QDateTimeEdit::YearSection:
|
||||||
|
return startDate.addYears(steps);
|
||||||
|
default:
|
||||||
|
qFatal("Unexpected section");
|
||||||
|
return QDate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static QTime stepTime(const QTime& startTime, const QDateTimeEdit::Section section,
|
||||||
|
const int steps)
|
||||||
|
{
|
||||||
|
switch (section) {
|
||||||
|
case QDateTimeEdit::SecondSection:
|
||||||
|
return startTime.addSecs(steps);
|
||||||
|
case QDateTimeEdit::MinuteSection:
|
||||||
|
return QTime(startTime.hour(),
|
||||||
|
startTime.minute() + steps,
|
||||||
|
startTime.second());
|
||||||
|
case QDateTimeEdit::HourSection:
|
||||||
|
return QTime(startTime.hour() + steps,
|
||||||
|
startTime.minute(),
|
||||||
|
startTime.second());
|
||||||
|
default:
|
||||||
|
qFatal("Unexpected section");
|
||||||
|
return QTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Testing get/set functions
|
// Testing get/set functions
|
||||||
void tst_QDateTimeEdit::getSetCheck()
|
void tst_QDateTimeEdit::getSetCheck()
|
||||||
{
|
{
|
||||||
@ -3000,20 +3105,154 @@ void tst_QDateTimeEdit::ddMMMMyyyy()
|
|||||||
QCOMPARE(testWidget->lineEdit()->text(), "01." + QDate::longMonthName(1) + ".200");
|
QCOMPARE(testWidget->lineEdit()->text(), "01." + QDate::longMonthName(1) + ".200");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QDateTimeEdit::wheelEvent_data()
|
||||||
|
{
|
||||||
#if QT_CONFIG(wheelevent)
|
#if QT_CONFIG(wheelevent)
|
||||||
|
QTest::addColumn<QPoint>("angleDelta");
|
||||||
|
QTest::addColumn<int>("qt4Delta");
|
||||||
|
QTest::addColumn<Qt::KeyboardModifiers>("modifiers");
|
||||||
|
QTest::addColumn<Qt::MouseEventSource>("source");
|
||||||
|
QTest::addColumn<QDateTimeEdit::Section>("section");
|
||||||
|
QTest::addColumn<QDate>("startDate");
|
||||||
|
QTest::addColumn<DateList>("expectedDates");
|
||||||
|
|
||||||
|
const auto fractions = {false, true};
|
||||||
|
|
||||||
|
const auto directions = {true, false};
|
||||||
|
|
||||||
|
const auto modifierList = {Qt::NoModifier,
|
||||||
|
Qt::ControlModifier,
|
||||||
|
Qt::ShiftModifier};
|
||||||
|
|
||||||
|
const auto sources = {Qt::MouseEventNotSynthesized,
|
||||||
|
Qt::MouseEventSynthesizedBySystem,
|
||||||
|
Qt::MouseEventSynthesizedByQt,
|
||||||
|
Qt::MouseEventSynthesizedByApplication};
|
||||||
|
|
||||||
|
const auto sections = {QDateTimeEdit::DaySection,
|
||||||
|
QDateTimeEdit::MonthSection,
|
||||||
|
QDateTimeEdit::YearSection};
|
||||||
|
|
||||||
|
for (auto fraction : fractions) {
|
||||||
|
for (auto up : directions) {
|
||||||
|
|
||||||
|
const QDate startDate(2000, up ? 2 : 12, 17);
|
||||||
|
|
||||||
|
const int units = (fraction ? 60 : 120) * (up ? 1 : -1);
|
||||||
|
|
||||||
|
for (auto modifier : modifierList) {
|
||||||
|
|
||||||
|
const Qt::KeyboardModifiers modifiers(modifier);
|
||||||
|
|
||||||
|
const auto modifierName = modifierToName(modifier);
|
||||||
|
if (modifierName.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const int steps = (modifier & Qt::ControlModifier ? 10 : 1)
|
||||||
|
* (up ? 1 : -1);
|
||||||
|
|
||||||
|
for (auto source : sources) {
|
||||||
|
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
QPoint angleDelta;
|
||||||
|
if ((modifier & Qt::ShiftModifier) &&
|
||||||
|
source == Qt::MouseEventNotSynthesized) {
|
||||||
|
// On macOS the Shift modifier converts vertical
|
||||||
|
// mouse wheel events to horizontal.
|
||||||
|
angleDelta = { units, 0 };
|
||||||
|
} else {
|
||||||
|
// However, this is not the case for trackpad scroll
|
||||||
|
// events.
|
||||||
|
angleDelta = { 0, units };
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
const QPoint angleDelta(0, units);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QLatin1String sourceName;
|
||||||
|
switch (source) {
|
||||||
|
case Qt::MouseEventNotSynthesized:
|
||||||
|
sourceName = QLatin1Literal("NotSynthesized");
|
||||||
|
break;
|
||||||
|
case Qt::MouseEventSynthesizedBySystem:
|
||||||
|
sourceName = QLatin1Literal("SynthesizedBySystem");
|
||||||
|
break;
|
||||||
|
case Qt::MouseEventSynthesizedByQt:
|
||||||
|
sourceName = QLatin1Literal("SynthesizedByQt");
|
||||||
|
break;
|
||||||
|
case Qt::MouseEventSynthesizedByApplication:
|
||||||
|
sourceName = QLatin1Literal("SynthesizedByApplication");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qFatal("Unexpected wheel event source");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto section : sections) {
|
||||||
|
|
||||||
|
DateList expectedDates;
|
||||||
|
if (fraction)
|
||||||
|
expectedDates << startDate;
|
||||||
|
|
||||||
|
const auto expectedDate = stepDate(startDate, section, steps);
|
||||||
|
if (!expectedDate.isValid())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
expectedDates << expectedDate;
|
||||||
|
|
||||||
|
const QLatin1String sectionName = sectionToName(section);
|
||||||
|
|
||||||
|
QTest::addRow("%s%s%sWith%sKeyboardModifier%s",
|
||||||
|
fraction ? "half" : "full",
|
||||||
|
up ? "Up" : "Down",
|
||||||
|
sectionName.latin1(),
|
||||||
|
modifierName.latin1(),
|
||||||
|
sourceName.latin1())
|
||||||
|
<< angleDelta
|
||||||
|
<< units
|
||||||
|
<< modifiers
|
||||||
|
<< source
|
||||||
|
<< section
|
||||||
|
<< startDate
|
||||||
|
<< expectedDates;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
QSKIP("Built with --no-feature-wheelevent");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void tst_QDateTimeEdit::wheelEvent()
|
void tst_QDateTimeEdit::wheelEvent()
|
||||||
{
|
{
|
||||||
testWidget->setDisplayFormat("dddd/MM");
|
#if QT_CONFIG(wheelevent)
|
||||||
testWidget->setDate(QDate(2000, 2, 21));
|
QFETCH(QPoint, angleDelta);
|
||||||
testWidget->setCurrentSection(QDateTimeEdit::DaySection);
|
QFETCH(int, qt4Delta);
|
||||||
QWheelEvent w(testWidget->lineEdit()->geometry().center(), 120, 0, 0);
|
QFETCH(Qt::KeyboardModifiers, modifiers);
|
||||||
qApp->sendEvent(testWidget, &w);
|
QFETCH(Qt::MouseEventSource, source);
|
||||||
QCOMPARE(testWidget->date(), QDate(2000, 2, 22));
|
QFETCH(QDateTimeEdit::Section, section);
|
||||||
testWidget->setCurrentSection(QDateTimeEdit::MonthSection);
|
QFETCH(QDate, startDate);
|
||||||
qApp->sendEvent(testWidget, &w);
|
QFETCH(DateList, expectedDates);
|
||||||
QCOMPARE(testWidget->date(), QDate(2000, 3, 22));
|
|
||||||
}
|
EditorDateEdit edit(0);
|
||||||
|
edit.setDate(startDate);
|
||||||
|
edit.setCurrentSection(section);
|
||||||
|
|
||||||
|
QWheelEvent event(QPointF(), QPointF(), QPoint(), angleDelta, qt4Delta,
|
||||||
|
Qt::Vertical, Qt::NoButton, modifiers, Qt::NoScrollPhase,
|
||||||
|
source);
|
||||||
|
|
||||||
|
QCOMPARE(edit.date(), startDate);
|
||||||
|
for (QDate expected : expectedDates) {
|
||||||
|
qApp->sendEvent(&edit, &event);
|
||||||
|
QCOMPARE(edit.date(), expected);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
QSKIP("Built with --no-feature-wheelevent");
|
||||||
#endif // QT_CONFIG(wheelevent)
|
#endif // QT_CONFIG(wheelevent)
|
||||||
|
}
|
||||||
|
|
||||||
void tst_QDateTimeEdit::specialValueCornerCase()
|
void tst_QDateTimeEdit::specialValueCornerCase()
|
||||||
{
|
{
|
||||||
@ -3735,5 +3974,238 @@ void tst_QDateTimeEdit::dateEditCorrectSectionSize()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void tst_QDateTimeEdit::stepModifierKeys_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QDate>("startDate");
|
||||||
|
QTest::addColumn<QDateTimeEdit::Section>("section");
|
||||||
|
QTest::addColumn<QTestEventList>("keys");
|
||||||
|
QTest::addColumn<QDate>("expectedDate");
|
||||||
|
|
||||||
|
const auto keyList = {Qt::Key_Up, Qt::Key_Down};
|
||||||
|
|
||||||
|
const auto modifierList = {Qt::NoModifier,
|
||||||
|
Qt::ControlModifier,
|
||||||
|
Qt::ShiftModifier};
|
||||||
|
|
||||||
|
const auto sections = {QDateTimeEdit::DaySection,
|
||||||
|
QDateTimeEdit::MonthSection,
|
||||||
|
QDateTimeEdit::YearSection};
|
||||||
|
|
||||||
|
for (auto key : keyList) {
|
||||||
|
|
||||||
|
const bool up = key == Qt::Key_Up;
|
||||||
|
Q_ASSERT(up || key == Qt::Key_Down);
|
||||||
|
|
||||||
|
const QDate startDate(2000, up ? 2 : 12, 17);
|
||||||
|
|
||||||
|
for (auto modifier : modifierList) {
|
||||||
|
|
||||||
|
QTestEventList keys;
|
||||||
|
keys.addKeyClick(key, modifier);
|
||||||
|
|
||||||
|
const auto modifierName = modifierToName(modifier);
|
||||||
|
if (modifierName.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const int steps = (modifier & Qt::ControlModifier ? 10 : 1)
|
||||||
|
* (up ? 1 : -1);
|
||||||
|
|
||||||
|
for (const auto section : sections) {
|
||||||
|
|
||||||
|
const auto expectedDate = stepDate(startDate, section, steps);
|
||||||
|
if (!expectedDate.isValid())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto sectionName = sectionToName(section);
|
||||||
|
|
||||||
|
QTest::addRow("%s%sWith%sKeyboardModifier",
|
||||||
|
up ? "up" : "down",
|
||||||
|
sectionName.latin1(),
|
||||||
|
modifierName.latin1())
|
||||||
|
<< startDate
|
||||||
|
<< section
|
||||||
|
<< keys
|
||||||
|
<< expectedDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QDateTimeEdit::stepModifierKeys()
|
||||||
|
{
|
||||||
|
QFETCH(QDate, startDate);
|
||||||
|
QFETCH(QDateTimeEdit::Section, section);
|
||||||
|
QFETCH(QTestEventList, keys);
|
||||||
|
QFETCH(QDate, expectedDate);
|
||||||
|
|
||||||
|
QDateTimeEdit edit(0);
|
||||||
|
edit.setDate(startDate);
|
||||||
|
edit.show();
|
||||||
|
QVERIFY(QTest::qWaitForWindowActive(&edit));
|
||||||
|
edit.setCurrentSection(section);
|
||||||
|
|
||||||
|
QCOMPARE(edit.date(), startDate);
|
||||||
|
keys.simulate(&edit);
|
||||||
|
QCOMPARE(edit.date(), expectedDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QDateTimeEdit::stepModifierButtons_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QStyle::SubControl>("subControl");
|
||||||
|
QTest::addColumn<Qt::KeyboardModifiers>("modifiers");
|
||||||
|
QTest::addColumn<QDateTimeEdit::Section>("section");
|
||||||
|
QTest::addColumn<QTime>("startTime");
|
||||||
|
QTest::addColumn<QTime>("expectedTime");
|
||||||
|
|
||||||
|
const auto subControls = {QStyle::SC_SpinBoxUp, QStyle::SC_SpinBoxDown};
|
||||||
|
|
||||||
|
const auto modifierList = {Qt::NoModifier,
|
||||||
|
Qt::ControlModifier,
|
||||||
|
Qt::ShiftModifier};
|
||||||
|
|
||||||
|
const auto sections = {QDateTimeEdit::SecondSection,
|
||||||
|
QDateTimeEdit::MinuteSection,
|
||||||
|
QDateTimeEdit::HourSection};
|
||||||
|
|
||||||
|
const QTime startTime(12, 36, 24);
|
||||||
|
|
||||||
|
for (auto subControl : subControls) {
|
||||||
|
|
||||||
|
const bool up = subControl == QStyle::SC_SpinBoxUp;
|
||||||
|
Q_ASSERT(up || subControl == QStyle::SC_SpinBoxDown);
|
||||||
|
|
||||||
|
for (auto modifier : modifierList) {
|
||||||
|
|
||||||
|
const Qt::KeyboardModifiers modifiers(modifier);
|
||||||
|
|
||||||
|
const auto modifierName = modifierToName(modifier);
|
||||||
|
if (modifierName.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const int steps = (modifier & Qt::ControlModifier ? 10 : 1)
|
||||||
|
* (up ? 1 : -1);
|
||||||
|
|
||||||
|
for (const auto section : sections) {
|
||||||
|
|
||||||
|
const auto expectedTime = stepTime(startTime, section, steps);
|
||||||
|
if (!expectedTime.isValid())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto sectionName = sectionToName(section);
|
||||||
|
|
||||||
|
QTest::addRow("%s%sWith%sKeyboardModifier",
|
||||||
|
up ? "up" : "down",
|
||||||
|
sectionName.latin1(),
|
||||||
|
modifierName.latin1())
|
||||||
|
<< subControl
|
||||||
|
<< modifiers
|
||||||
|
<< section
|
||||||
|
<< startTime
|
||||||
|
<< expectedTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QDateTimeEdit::stepModifierButtons()
|
||||||
|
{
|
||||||
|
QFETCH(QStyle::SubControl, subControl);
|
||||||
|
QFETCH(Qt::KeyboardModifiers, modifiers);
|
||||||
|
QFETCH(QDateTimeEdit::Section, section);
|
||||||
|
QFETCH(QTime, startTime);
|
||||||
|
QFETCH(QTime, expectedTime);
|
||||||
|
|
||||||
|
EditorDateEdit edit(0);
|
||||||
|
edit.setTime(startTime);
|
||||||
|
edit.show();
|
||||||
|
QVERIFY(QTest::qWaitForWindowActive(&edit));
|
||||||
|
edit.setCurrentSection(section);
|
||||||
|
|
||||||
|
QStyleOptionSpinBox spinBoxStyleOption;
|
||||||
|
edit.initStyleOption(&spinBoxStyleOption);
|
||||||
|
|
||||||
|
const QRect buttonRect = edit.style()->subControlRect(
|
||||||
|
QStyle::CC_SpinBox, &spinBoxStyleOption, subControl, &edit);
|
||||||
|
|
||||||
|
QCOMPARE(edit.time(), startTime);
|
||||||
|
QTest::mouseClick(&edit, Qt::LeftButton, modifiers, buttonRect.center());
|
||||||
|
QCOMPARE(edit.time(), expectedTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QDateTimeEdit::stepModifierPressAndHold_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QStyle::SubControl>("subControl");
|
||||||
|
QTest::addColumn<Qt::KeyboardModifiers>("modifiers");
|
||||||
|
QTest::addColumn<int>("expectedStepModifier");
|
||||||
|
|
||||||
|
const auto subControls = {QStyle::SC_SpinBoxUp, QStyle::SC_SpinBoxDown};
|
||||||
|
|
||||||
|
const auto modifierList = {Qt::NoModifier,
|
||||||
|
Qt::ControlModifier,
|
||||||
|
Qt::ShiftModifier};
|
||||||
|
|
||||||
|
for (auto subControl : subControls) {
|
||||||
|
|
||||||
|
const bool up = subControl == QStyle::SC_SpinBoxUp;
|
||||||
|
Q_ASSERT(up || subControl == QStyle::SC_SpinBoxDown);
|
||||||
|
|
||||||
|
for (auto modifier : modifierList) {
|
||||||
|
|
||||||
|
const Qt::KeyboardModifiers modifiers(modifier);
|
||||||
|
|
||||||
|
const auto modifierName = modifierToName(modifier);
|
||||||
|
if (modifierName.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const int steps = (modifier & Qt::ControlModifier ? 10 : 1)
|
||||||
|
* (up ? 1 : -1);
|
||||||
|
|
||||||
|
QTest::addRow("%sWith%sKeyboardModifier",
|
||||||
|
up ? "up" : "down",
|
||||||
|
modifierName.latin1())
|
||||||
|
<< subControl
|
||||||
|
<< modifiers
|
||||||
|
<< steps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QDateTimeEdit::stepModifierPressAndHold()
|
||||||
|
{
|
||||||
|
QFETCH(QStyle::SubControl, subControl);
|
||||||
|
QFETCH(Qt::KeyboardModifiers, modifiers);
|
||||||
|
QFETCH(int, expectedStepModifier);
|
||||||
|
|
||||||
|
const QDate startDate(2000, 1, 1);
|
||||||
|
|
||||||
|
EditorDateEdit edit(0);
|
||||||
|
QScopedPointer<PressAndHoldStyle, QScopedPointerDeleteLater> pressAndHoldStyle(
|
||||||
|
new PressAndHoldStyle);
|
||||||
|
edit.setStyle(pressAndHoldStyle.data());
|
||||||
|
edit.setDate(startDate);
|
||||||
|
|
||||||
|
QSignalSpy spy(&edit, &EditorDateEdit::dateChanged);
|
||||||
|
|
||||||
|
edit.show();
|
||||||
|
QVERIFY(QTest::qWaitForWindowActive(&edit));
|
||||||
|
edit.setCurrentSection(QDateTimeEdit::YearSection);
|
||||||
|
|
||||||
|
QStyleOptionSpinBox spinBoxStyleOption;
|
||||||
|
edit.initStyleOption(&spinBoxStyleOption);
|
||||||
|
|
||||||
|
const QRect buttonRect = edit.style()->subControlRect(
|
||||||
|
QStyle::CC_SpinBox, &spinBoxStyleOption, subControl, &edit);
|
||||||
|
|
||||||
|
QTest::mousePress(&edit, Qt::LeftButton, modifiers, buttonRect.center());
|
||||||
|
QTRY_VERIFY(spy.length() >= 3);
|
||||||
|
QTest::mouseRelease(&edit, Qt::LeftButton, modifiers, buttonRect.center());
|
||||||
|
|
||||||
|
const auto value = spy.last().at(0);
|
||||||
|
QVERIFY(value.type() == QVariant::Date);
|
||||||
|
const QDate expectedDate = startDate.addYears(spy.length() *
|
||||||
|
expectedStepModifier);
|
||||||
|
QCOMPARE(value.toDate(), expectedDate);
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_MAIN(tst_QDateTimeEdit)
|
QTEST_MAIN(tst_QDateTimeEdit)
|
||||||
#include "tst_qdatetimeedit.moc"
|
#include "tst_qdatetimeedit.moc"
|
||||||
|
@ -41,6 +41,10 @@
|
|||||||
#include <qlineedit.h>
|
#include <qlineedit.h>
|
||||||
#include <qdebug.h>
|
#include <qdebug.h>
|
||||||
|
|
||||||
|
#include <QStyleOptionSpinBox>
|
||||||
|
#include <QStyle>
|
||||||
|
#include <QProxyStyle>
|
||||||
|
|
||||||
class DoubleSpinBox : public QDoubleSpinBox
|
class DoubleSpinBox : public QDoubleSpinBox
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -60,6 +64,16 @@ public:
|
|||||||
{
|
{
|
||||||
return QDoubleSpinBox::valueFromText(text);
|
return QDoubleSpinBox::valueFromText(text);
|
||||||
}
|
}
|
||||||
|
#if QT_CONFIG(wheelevent)
|
||||||
|
void wheelEvent(QWheelEvent *event)
|
||||||
|
{
|
||||||
|
QDoubleSpinBox::wheelEvent(event);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
void initStyleOption(QStyleOptionSpinBox *option) const
|
||||||
|
{
|
||||||
|
QDoubleSpinBox::initStyleOption(option);
|
||||||
|
}
|
||||||
|
|
||||||
QLineEdit* lineEdit() const { return QDoubleSpinBox::lineEdit(); }
|
QLineEdit* lineEdit() const { return QDoubleSpinBox::lineEdit(); }
|
||||||
public slots:
|
public slots:
|
||||||
@ -69,6 +83,26 @@ public slots:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PressAndHoldStyle : public QProxyStyle
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
using QProxyStyle::QProxyStyle;
|
||||||
|
|
||||||
|
int styleHint(QStyle::StyleHint hint, const QStyleOption *option = nullptr,
|
||||||
|
const QWidget *widget = nullptr, QStyleHintReturn *returnData = nullptr) const override
|
||||||
|
{
|
||||||
|
switch (hint) {
|
||||||
|
case QStyle::SH_SpinBox_ClickAutoRepeatRate:
|
||||||
|
return 5;
|
||||||
|
case QStyle::SH_SpinBox_ClickAutoRepeatThreshold:
|
||||||
|
return 10;
|
||||||
|
default:
|
||||||
|
return QProxyStyle::styleHint(hint, option, widget, returnData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class tst_QDoubleSpinBox : public QObject
|
class tst_QDoubleSpinBox : public QObject
|
||||||
{
|
{
|
||||||
@ -138,6 +172,17 @@ private slots:
|
|||||||
|
|
||||||
void adaptiveDecimalStep();
|
void adaptiveDecimalStep();
|
||||||
|
|
||||||
|
void wheelEvents_data();
|
||||||
|
void wheelEvents();
|
||||||
|
|
||||||
|
void stepModifierKeys_data();
|
||||||
|
void stepModifierKeys();
|
||||||
|
|
||||||
|
void stepModifierButtons_data();
|
||||||
|
void stepModifierButtons();
|
||||||
|
|
||||||
|
void stepModifierPressAndHold_data();
|
||||||
|
void stepModifierPressAndHold();
|
||||||
public slots:
|
public slots:
|
||||||
void valueChangedHelper(const QString &);
|
void valueChangedHelper(const QString &);
|
||||||
void valueChangedHelper(double);
|
void valueChangedHelper(double);
|
||||||
@ -152,6 +197,24 @@ typedef QList<double> DoubleList;
|
|||||||
Q_DECLARE_METATYPE(QLocale::Language)
|
Q_DECLARE_METATYPE(QLocale::Language)
|
||||||
Q_DECLARE_METATYPE(QLocale::Country)
|
Q_DECLARE_METATYPE(QLocale::Country)
|
||||||
|
|
||||||
|
static QLatin1String modifierToName(Qt::KeyboardModifier modifier)
|
||||||
|
{
|
||||||
|
switch (modifier) {
|
||||||
|
case Qt::NoModifier:
|
||||||
|
return QLatin1Literal("No");
|
||||||
|
break;
|
||||||
|
case Qt::ControlModifier:
|
||||||
|
return QLatin1Literal("Ctrl");
|
||||||
|
break;
|
||||||
|
case Qt::ShiftModifier:
|
||||||
|
return QLatin1Literal("Shift");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qFatal("Unexpected keyboard modifier");
|
||||||
|
return QLatin1String();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tst_QDoubleSpinBox::tst_QDoubleSpinBox()
|
tst_QDoubleSpinBox::tst_QDoubleSpinBox()
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -1270,5 +1333,332 @@ void tst_QDoubleSpinBox::adaptiveDecimalStep()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QDoubleSpinBox::wheelEvents_data()
|
||||||
|
{
|
||||||
|
#if QT_CONFIG(wheelevent)
|
||||||
|
QTest::addColumn<QPoint>("angleDelta");
|
||||||
|
QTest::addColumn<int>("qt4Delta");
|
||||||
|
QTest::addColumn<Qt::KeyboardModifiers>("modifier");
|
||||||
|
QTest::addColumn<Qt::MouseEventSource>("source");
|
||||||
|
QTest::addColumn<double>("start");
|
||||||
|
QTest::addColumn<DoubleList>("expectedValues");
|
||||||
|
|
||||||
|
const auto fractions = {false, true};
|
||||||
|
|
||||||
|
const auto directions = {true, false};
|
||||||
|
|
||||||
|
const auto modifierList = {Qt::NoModifier,
|
||||||
|
Qt::ControlModifier,
|
||||||
|
Qt::ShiftModifier};
|
||||||
|
|
||||||
|
const auto sources = {Qt::MouseEventNotSynthesized,
|
||||||
|
Qt::MouseEventSynthesizedBySystem,
|
||||||
|
Qt::MouseEventSynthesizedByQt,
|
||||||
|
Qt::MouseEventSynthesizedByApplication};
|
||||||
|
|
||||||
|
const double startValue = 0.0;
|
||||||
|
|
||||||
|
for (auto fraction : fractions) {
|
||||||
|
for (auto up : directions) {
|
||||||
|
|
||||||
|
const int units = (fraction ? 60 : 120) * (up ? 1 : -1);
|
||||||
|
|
||||||
|
for (auto modifier : modifierList) {
|
||||||
|
|
||||||
|
const Qt::KeyboardModifiers modifiers(modifier);
|
||||||
|
|
||||||
|
const auto modifierName = modifierToName(modifier);
|
||||||
|
if (modifierName.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const int steps = (modifier & Qt::ControlModifier ? 10 : 1)
|
||||||
|
* (up ? 1 : -1);
|
||||||
|
|
||||||
|
for (auto source : sources) {
|
||||||
|
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
QPoint angleDelta;
|
||||||
|
if ((modifier & Qt::ShiftModifier) &&
|
||||||
|
source == Qt::MouseEventNotSynthesized) {
|
||||||
|
// On macOS the Shift modifier converts vertical
|
||||||
|
// mouse wheel events to horizontal.
|
||||||
|
angleDelta = { units, 0 };
|
||||||
|
} else {
|
||||||
|
// However, this is not the case for trackpad scroll
|
||||||
|
// events.
|
||||||
|
angleDelta = { 0, units };
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
const QPoint angleDelta(0, units);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QLatin1String sourceName;
|
||||||
|
switch (source) {
|
||||||
|
case Qt::MouseEventNotSynthesized:
|
||||||
|
sourceName = QLatin1Literal("NotSynthesized");
|
||||||
|
break;
|
||||||
|
case Qt::MouseEventSynthesizedBySystem:
|
||||||
|
sourceName = QLatin1Literal("SynthesizedBySystem");
|
||||||
|
break;
|
||||||
|
case Qt::MouseEventSynthesizedByQt:
|
||||||
|
sourceName = QLatin1Literal("SynthesizedByQt");
|
||||||
|
break;
|
||||||
|
case Qt::MouseEventSynthesizedByApplication:
|
||||||
|
sourceName = QLatin1Literal("SynthesizedByApplication");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qFatal("Unexpected wheel event source");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DoubleList expectedValues;
|
||||||
|
if (fraction)
|
||||||
|
expectedValues << startValue;
|
||||||
|
expectedValues << startValue + steps;
|
||||||
|
|
||||||
|
QTest::addRow("%s%sWith%sKeyboardModifier%s",
|
||||||
|
fraction ? "half" : "full",
|
||||||
|
up ? "Up" : "Down",
|
||||||
|
modifierName.latin1(),
|
||||||
|
sourceName.latin1())
|
||||||
|
<< angleDelta
|
||||||
|
<< units
|
||||||
|
<< modifiers
|
||||||
|
<< source
|
||||||
|
<< startValue
|
||||||
|
<< expectedValues;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
QSKIP("Built with --no-feature-wheelevent");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QDoubleSpinBox::wheelEvents()
|
||||||
|
{
|
||||||
|
#if QT_CONFIG(wheelevent)
|
||||||
|
QFETCH(QPoint, angleDelta);
|
||||||
|
QFETCH(int, qt4Delta);
|
||||||
|
QFETCH(Qt::KeyboardModifiers, modifier);
|
||||||
|
QFETCH(Qt::MouseEventSource, source);
|
||||||
|
QFETCH(double, start);
|
||||||
|
QFETCH(DoubleList, expectedValues);
|
||||||
|
|
||||||
|
DoubleSpinBox spinBox;
|
||||||
|
spinBox.setRange(-20, 20);
|
||||||
|
spinBox.setValue(start);
|
||||||
|
|
||||||
|
QWheelEvent event(QPointF(), QPointF(), QPoint(), angleDelta, qt4Delta,
|
||||||
|
Qt::Vertical, Qt::NoButton, modifier, Qt::NoScrollPhase,
|
||||||
|
source);
|
||||||
|
for (int expected : expectedValues) {
|
||||||
|
qApp->sendEvent(&spinBox, &event);
|
||||||
|
QCOMPARE(spinBox.value(), expected);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
QSKIP("Built with --no-feature-wheelevent");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QDoubleSpinBox::stepModifierKeys_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<double>("startValue");
|
||||||
|
QTest::addColumn<QTestEventList>("keys");
|
||||||
|
QTest::addColumn<double>("expectedValue");
|
||||||
|
|
||||||
|
const auto keyList = {Qt::Key_Up, Qt::Key_Down};
|
||||||
|
|
||||||
|
const auto modifierList = {Qt::NoModifier,
|
||||||
|
Qt::ControlModifier,
|
||||||
|
Qt::ShiftModifier};
|
||||||
|
|
||||||
|
for (auto key : keyList) {
|
||||||
|
|
||||||
|
const bool up = key == Qt::Key_Up;
|
||||||
|
Q_ASSERT(up || key == Qt::Key_Down);
|
||||||
|
|
||||||
|
const double startValue = up ? 0.0 : 10.0;
|
||||||
|
|
||||||
|
for (auto modifier : modifierList) {
|
||||||
|
|
||||||
|
QTestEventList keys;
|
||||||
|
keys.addKeyClick(key, modifier);
|
||||||
|
|
||||||
|
const auto modifierName = modifierToName(modifier);
|
||||||
|
if (modifierName.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const int steps = (modifier & Qt::ControlModifier ? 10 : 1)
|
||||||
|
* (up ? 1 : -1);
|
||||||
|
|
||||||
|
const double expectedValue = startValue + steps;
|
||||||
|
|
||||||
|
QTest::addRow("%sWith%sKeyboardModifier",
|
||||||
|
up ? "up" : "down",
|
||||||
|
modifierName.latin1())
|
||||||
|
<< startValue
|
||||||
|
<< keys
|
||||||
|
<< expectedValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QDoubleSpinBox::stepModifierKeys()
|
||||||
|
{
|
||||||
|
QFETCH(double, startValue);
|
||||||
|
QFETCH(QTestEventList, keys);
|
||||||
|
QFETCH(double, expectedValue);
|
||||||
|
|
||||||
|
QDoubleSpinBox spin(0);
|
||||||
|
spin.setValue(startValue);
|
||||||
|
spin.show();
|
||||||
|
QVERIFY(QTest::qWaitForWindowActive(&spin));
|
||||||
|
|
||||||
|
QCOMPARE(spin.value(), startValue);
|
||||||
|
keys.simulate(&spin);
|
||||||
|
QCOMPARE(spin.value(), expectedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QDoubleSpinBox::stepModifierButtons_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QStyle::SubControl>("subControl");
|
||||||
|
QTest::addColumn<Qt::KeyboardModifiers>("modifiers");
|
||||||
|
QTest::addColumn<double>("startValue");
|
||||||
|
QTest::addColumn<double>("expectedValue");
|
||||||
|
|
||||||
|
const auto subControls = {QStyle::SC_SpinBoxUp, QStyle::SC_SpinBoxDown};
|
||||||
|
|
||||||
|
const auto modifierList = {Qt::NoModifier,
|
||||||
|
Qt::ControlModifier,
|
||||||
|
Qt::ShiftModifier};
|
||||||
|
|
||||||
|
for (auto subControl : subControls) {
|
||||||
|
|
||||||
|
const bool up = subControl == QStyle::SC_SpinBoxUp;
|
||||||
|
Q_ASSERT(up || subControl == QStyle::SC_SpinBoxDown);
|
||||||
|
|
||||||
|
const double startValue = up ? 0 : 10;
|
||||||
|
|
||||||
|
for (auto modifier : modifierList) {
|
||||||
|
|
||||||
|
const Qt::KeyboardModifiers modifiers(modifier);
|
||||||
|
|
||||||
|
const auto modifierName = modifierToName(modifier);
|
||||||
|
if (modifierName.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const int steps = (modifier & Qt::ControlModifier ? 10 : 1)
|
||||||
|
* (up ? 1 : -1);
|
||||||
|
|
||||||
|
const double expectedValue = startValue + steps;
|
||||||
|
|
||||||
|
QTest::addRow("%sWith%sKeyboardModifier",
|
||||||
|
up ? "up" : "down",
|
||||||
|
modifierName.latin1())
|
||||||
|
<< subControl
|
||||||
|
<< modifiers
|
||||||
|
<< startValue
|
||||||
|
<< expectedValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QDoubleSpinBox::stepModifierButtons()
|
||||||
|
{
|
||||||
|
QFETCH(QStyle::SubControl, subControl);
|
||||||
|
QFETCH(Qt::KeyboardModifiers, modifiers);
|
||||||
|
QFETCH(double, startValue);
|
||||||
|
QFETCH(double, expectedValue);
|
||||||
|
|
||||||
|
DoubleSpinBox spin(0);
|
||||||
|
spin.setRange(-20, 20);
|
||||||
|
spin.setValue(startValue);
|
||||||
|
spin.show();
|
||||||
|
QVERIFY(QTest::qWaitForWindowActive(&spin));
|
||||||
|
|
||||||
|
QStyleOptionSpinBox spinBoxStyleOption;
|
||||||
|
spin.initStyleOption(&spinBoxStyleOption);
|
||||||
|
|
||||||
|
const QRect buttonRect = spin.style()->subControlRect(
|
||||||
|
QStyle::CC_SpinBox, &spinBoxStyleOption, subControl, &spin);
|
||||||
|
|
||||||
|
QCOMPARE(spin.value(), startValue);
|
||||||
|
QTest::mouseClick(&spin, Qt::LeftButton, modifiers, buttonRect.center());
|
||||||
|
QCOMPARE(spin.value(), expectedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QDoubleSpinBox::stepModifierPressAndHold_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QStyle::SubControl>("subControl");
|
||||||
|
QTest::addColumn<Qt::KeyboardModifiers>("modifiers");
|
||||||
|
QTest::addColumn<int>("expectedStepModifier");
|
||||||
|
|
||||||
|
const auto subControls = {QStyle::SC_SpinBoxUp, QStyle::SC_SpinBoxDown};
|
||||||
|
|
||||||
|
const auto modifierList = {Qt::NoModifier,
|
||||||
|
Qt::ControlModifier,
|
||||||
|
Qt::ShiftModifier};
|
||||||
|
|
||||||
|
for (auto subControl : subControls) {
|
||||||
|
|
||||||
|
const bool up = subControl == QStyle::SC_SpinBoxUp;
|
||||||
|
Q_ASSERT(up || subControl == QStyle::SC_SpinBoxDown);
|
||||||
|
|
||||||
|
for (auto modifier : modifierList) {
|
||||||
|
|
||||||
|
const Qt::KeyboardModifiers modifiers(modifier);
|
||||||
|
|
||||||
|
const auto modifierName = modifierToName(modifier);
|
||||||
|
if (modifierName.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const int steps = (modifier & Qt::ControlModifier ? 10 : 1)
|
||||||
|
* (up ? 1 : -1);
|
||||||
|
|
||||||
|
QTest::addRow("%sWith%sKeyboardModifier",
|
||||||
|
up ? "up" : "down",
|
||||||
|
modifierName.latin1())
|
||||||
|
<< subControl
|
||||||
|
<< modifiers
|
||||||
|
<< steps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QDoubleSpinBox::stepModifierPressAndHold()
|
||||||
|
{
|
||||||
|
QFETCH(QStyle::SubControl, subControl);
|
||||||
|
QFETCH(Qt::KeyboardModifiers, modifiers);
|
||||||
|
QFETCH(int, expectedStepModifier);
|
||||||
|
|
||||||
|
DoubleSpinBox spin(0);
|
||||||
|
QScopedPointer<PressAndHoldStyle, QScopedPointerDeleteLater> pressAndHoldStyle(
|
||||||
|
new PressAndHoldStyle);
|
||||||
|
spin.setStyle(pressAndHoldStyle.data());
|
||||||
|
spin.setRange(-100.0, 100.0);
|
||||||
|
spin.setValue(0.0);
|
||||||
|
|
||||||
|
QSignalSpy spy(&spin, QOverload<double>::of(&DoubleSpinBox::valueChanged));
|
||||||
|
|
||||||
|
spin.show();
|
||||||
|
QVERIFY(QTest::qWaitForWindowExposed(&spin));
|
||||||
|
|
||||||
|
QStyleOptionSpinBox spinBoxStyleOption;
|
||||||
|
spin.initStyleOption(&spinBoxStyleOption);
|
||||||
|
|
||||||
|
const QRect buttonRect = spin.style()->subControlRect(
|
||||||
|
QStyle::CC_SpinBox, &spinBoxStyleOption, subControl, &spin);
|
||||||
|
|
||||||
|
QTest::mousePress(&spin, Qt::LeftButton, modifiers, buttonRect.center());
|
||||||
|
QTRY_VERIFY(spy.length() >= 3);
|
||||||
|
QTest::mouseRelease(&spin, Qt::LeftButton, modifiers, buttonRect.center());
|
||||||
|
|
||||||
|
const auto value = spy.last().at(0);
|
||||||
|
QVERIFY(value.type() == QVariant::Double);
|
||||||
|
QCOMPARE(value.toDouble(), spy.length() * expectedStepModifier);
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_MAIN(tst_QDoubleSpinBox)
|
QTEST_MAIN(tst_QDoubleSpinBox)
|
||||||
#include "tst_qdoublespinbox.moc"
|
#include "tst_qdoublespinbox.moc"
|
||||||
|
@ -50,6 +50,9 @@
|
|||||||
#include <QKeySequence>
|
#include <QKeySequence>
|
||||||
#include <QStackedWidget>
|
#include <QStackedWidget>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QStyleOptionSpinBox>
|
||||||
|
#include <QStyle>
|
||||||
|
#include <QProxyStyle>
|
||||||
|
|
||||||
class SpinBox : public QSpinBox
|
class SpinBox : public QSpinBox
|
||||||
{
|
{
|
||||||
@ -75,10 +78,34 @@ public:
|
|||||||
QSpinBox::wheelEvent(event);
|
QSpinBox::wheelEvent(event);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
void initStyleOption(QStyleOptionSpinBox *option) const
|
||||||
|
{
|
||||||
|
QSpinBox::initStyleOption(option);
|
||||||
|
}
|
||||||
|
|
||||||
QLineEdit *lineEdit() const { return QSpinBox::lineEdit(); }
|
QLineEdit *lineEdit() const { return QSpinBox::lineEdit(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PressAndHoldStyle : public QProxyStyle
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
using QProxyStyle::QProxyStyle;
|
||||||
|
|
||||||
|
int styleHint(QStyle::StyleHint hint, const QStyleOption *option = nullptr,
|
||||||
|
const QWidget *widget = nullptr, QStyleHintReturn *returnData = nullptr) const override
|
||||||
|
{
|
||||||
|
switch (hint) {
|
||||||
|
case QStyle::SH_SpinBox_ClickAutoRepeatRate:
|
||||||
|
return 5;
|
||||||
|
case QStyle::SH_SpinBox_ClickAutoRepeatThreshold:
|
||||||
|
return 10;
|
||||||
|
default:
|
||||||
|
return QProxyStyle::styleHint(hint, option, widget, returnData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class tst_QSpinBox : public QObject
|
class tst_QSpinBox : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -143,10 +170,19 @@ private slots:
|
|||||||
void setGroupSeparatorShown_data();
|
void setGroupSeparatorShown_data();
|
||||||
void setGroupSeparatorShown();
|
void setGroupSeparatorShown();
|
||||||
|
|
||||||
|
void wheelEvents_data();
|
||||||
void wheelEvents();
|
void wheelEvents();
|
||||||
|
|
||||||
void adaptiveDecimalStep();
|
void adaptiveDecimalStep();
|
||||||
|
|
||||||
|
void stepModifierKeys_data();
|
||||||
|
void stepModifierKeys();
|
||||||
|
|
||||||
|
void stepModifierButtons_data();
|
||||||
|
void stepModifierButtons();
|
||||||
|
|
||||||
|
void stepModifierPressAndHold_data();
|
||||||
|
void stepModifierPressAndHold();
|
||||||
public slots:
|
public slots:
|
||||||
void valueChangedHelper(const QString &);
|
void valueChangedHelper(const QString &);
|
||||||
void valueChangedHelper(int);
|
void valueChangedHelper(int);
|
||||||
@ -160,6 +196,24 @@ typedef QList<int> IntList;
|
|||||||
Q_DECLARE_METATYPE(QLocale::Language)
|
Q_DECLARE_METATYPE(QLocale::Language)
|
||||||
Q_DECLARE_METATYPE(QLocale::Country)
|
Q_DECLARE_METATYPE(QLocale::Country)
|
||||||
|
|
||||||
|
static QLatin1String modifierToName(Qt::KeyboardModifier modifier)
|
||||||
|
{
|
||||||
|
switch (modifier) {
|
||||||
|
case Qt::NoModifier:
|
||||||
|
return QLatin1Literal("No");
|
||||||
|
break;
|
||||||
|
case Qt::ControlModifier:
|
||||||
|
return QLatin1Literal("Ctrl");
|
||||||
|
break;
|
||||||
|
case Qt::ShiftModifier:
|
||||||
|
return QLatin1Literal("Shift");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qFatal("Unexpected keyboard modifier");
|
||||||
|
return QLatin1String();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Testing get/set functions
|
// Testing get/set functions
|
||||||
void tst_QSpinBox::getSetCheck()
|
void tst_QSpinBox::getSetCheck()
|
||||||
{
|
{
|
||||||
@ -1217,27 +1271,132 @@ void tst_QSpinBox::setGroupSeparatorShown()
|
|||||||
QCOMPARE(spinBox.value()+1000, 33000);
|
QCOMPARE(spinBox.value()+1000, 33000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QSpinBox::wheelEvents_data()
|
||||||
|
{
|
||||||
|
#if QT_CONFIG(wheelevent)
|
||||||
|
QTest::addColumn<QPoint>("angleDelta");
|
||||||
|
QTest::addColumn<int>("qt4Delta");
|
||||||
|
QTest::addColumn<Qt::KeyboardModifiers>("modifier");
|
||||||
|
QTest::addColumn<Qt::MouseEventSource>("source");
|
||||||
|
QTest::addColumn<int>("start");
|
||||||
|
QTest::addColumn<IntList>("expectedValues");
|
||||||
|
|
||||||
|
const auto fractions = {false, true};
|
||||||
|
|
||||||
|
const auto directions = {true, false};
|
||||||
|
|
||||||
|
const auto modifierList = {Qt::NoModifier,
|
||||||
|
Qt::ControlModifier,
|
||||||
|
Qt::ShiftModifier};
|
||||||
|
|
||||||
|
const auto sources = {Qt::MouseEventNotSynthesized,
|
||||||
|
Qt::MouseEventSynthesizedBySystem,
|
||||||
|
Qt::MouseEventSynthesizedByQt,
|
||||||
|
Qt::MouseEventSynthesizedByApplication};
|
||||||
|
|
||||||
|
const int startValue = 0;
|
||||||
|
|
||||||
|
for (auto fraction : fractions) {
|
||||||
|
for (auto up : directions) {
|
||||||
|
|
||||||
|
const int units = (fraction ? 60 : 120) * (up ? 1 : -1);
|
||||||
|
|
||||||
|
for (auto modifier : modifierList) {
|
||||||
|
|
||||||
|
const Qt::KeyboardModifiers modifiers(modifier);
|
||||||
|
|
||||||
|
const auto modifierName = modifierToName(modifier);
|
||||||
|
if (modifierName.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const int steps = (modifier & Qt::ControlModifier ? 10 : 1)
|
||||||
|
* (up ? 1 : -1);
|
||||||
|
|
||||||
|
for (auto source : sources) {
|
||||||
|
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
QPoint angleDelta;
|
||||||
|
if ((modifier & Qt::ShiftModifier) &&
|
||||||
|
source == Qt::MouseEventNotSynthesized) {
|
||||||
|
// On macOS the Shift modifier converts vertical
|
||||||
|
// mouse wheel events to horizontal.
|
||||||
|
angleDelta = { units, 0 };
|
||||||
|
} else {
|
||||||
|
// However, this is not the case for trackpad scroll
|
||||||
|
// events.
|
||||||
|
angleDelta = { 0, units };
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
const QPoint angleDelta(0, units);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QLatin1String sourceName;
|
||||||
|
switch (source) {
|
||||||
|
case Qt::MouseEventNotSynthesized:
|
||||||
|
sourceName = QLatin1Literal("NotSynthesized");
|
||||||
|
break;
|
||||||
|
case Qt::MouseEventSynthesizedBySystem:
|
||||||
|
sourceName = QLatin1Literal("SynthesizedBySystem");
|
||||||
|
break;
|
||||||
|
case Qt::MouseEventSynthesizedByQt:
|
||||||
|
sourceName = QLatin1Literal("SynthesizedByQt");
|
||||||
|
break;
|
||||||
|
case Qt::MouseEventSynthesizedByApplication:
|
||||||
|
sourceName = QLatin1Literal("SynthesizedByApplication");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qFatal("Unexpected wheel event source");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntList expectedValues;
|
||||||
|
if (fraction)
|
||||||
|
expectedValues << startValue;
|
||||||
|
expectedValues << startValue + steps;
|
||||||
|
|
||||||
|
QTest::addRow("%s%sWith%sKeyboardModifier%s",
|
||||||
|
fraction ? "half" : "full",
|
||||||
|
up ? "Up" : "Down",
|
||||||
|
modifierName.latin1(),
|
||||||
|
sourceName.latin1())
|
||||||
|
<< angleDelta
|
||||||
|
<< units
|
||||||
|
<< modifiers
|
||||||
|
<< source
|
||||||
|
<< startValue
|
||||||
|
<< expectedValues;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
QSKIP("Built with --no-feature-wheelevent");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void tst_QSpinBox::wheelEvents()
|
void tst_QSpinBox::wheelEvents()
|
||||||
{
|
{
|
||||||
#if QT_CONFIG(wheelevent)
|
#if QT_CONFIG(wheelevent)
|
||||||
|
QFETCH(QPoint, angleDelta);
|
||||||
|
QFETCH(int, qt4Delta);
|
||||||
|
QFETCH(Qt::KeyboardModifiers, modifier);
|
||||||
|
QFETCH(Qt::MouseEventSource, source);
|
||||||
|
QFETCH(int, start);
|
||||||
|
QFETCH(IntList, expectedValues);
|
||||||
|
|
||||||
SpinBox spinBox;
|
SpinBox spinBox;
|
||||||
spinBox.setRange(-20, 20);
|
spinBox.setRange(-20, 20);
|
||||||
spinBox.setValue(0);
|
spinBox.setValue(start);
|
||||||
|
|
||||||
QWheelEvent wheelUp(QPointF(), QPointF(), QPoint(), QPoint(0, 120), 120, Qt::Vertical, Qt::NoButton, Qt::NoModifier);
|
QWheelEvent event(QPointF(), QPointF(), QPoint(), angleDelta, qt4Delta,
|
||||||
spinBox.wheelEvent(&wheelUp);
|
Qt::Vertical, Qt::NoButton, modifier, Qt::NoScrollPhase,
|
||||||
QCOMPARE(spinBox.value(), 1);
|
source);
|
||||||
|
for (int expected : expectedValues) {
|
||||||
QWheelEvent wheelDown(QPointF(), QPointF(), QPoint(), QPoint(0, -120), -120, Qt::Vertical, Qt::NoButton, Qt::NoModifier);
|
qApp->sendEvent(&spinBox, &event);
|
||||||
spinBox.wheelEvent(&wheelDown);
|
QCOMPARE(spinBox.value(), expected);
|
||||||
spinBox.wheelEvent(&wheelDown);
|
}
|
||||||
QCOMPARE(spinBox.value(), -1);
|
#else
|
||||||
|
QSKIP("Built with --no-feature-wheelevent");
|
||||||
QWheelEvent wheelHalfUp(QPointF(), QPointF(), QPoint(), QPoint(0, 60), 60, Qt::Vertical, Qt::NoButton, Qt::NoModifier);
|
|
||||||
spinBox.wheelEvent(&wheelHalfUp);
|
|
||||||
QCOMPARE(spinBox.value(), -1);
|
|
||||||
spinBox.wheelEvent(&wheelHalfUp);
|
|
||||||
QCOMPARE(spinBox.value(), 0);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1320,5 +1479,203 @@ void tst_QSpinBox::adaptiveDecimalStep()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QSpinBox::stepModifierKeys_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<int>("startValue");
|
||||||
|
QTest::addColumn<QTestEventList>("keys");
|
||||||
|
QTest::addColumn<int>("expectedValue");
|
||||||
|
|
||||||
|
const auto keyList = {Qt::Key_Up, Qt::Key_Down};
|
||||||
|
|
||||||
|
const auto modifierList = {Qt::NoModifier,
|
||||||
|
Qt::ControlModifier,
|
||||||
|
Qt::ShiftModifier};
|
||||||
|
|
||||||
|
for (auto key : keyList) {
|
||||||
|
|
||||||
|
const bool up = key == Qt::Key_Up;
|
||||||
|
Q_ASSERT(up || key == Qt::Key_Down);
|
||||||
|
|
||||||
|
const int startValue = up ? 0.0 : 10.0;
|
||||||
|
|
||||||
|
for (auto modifier : modifierList) {
|
||||||
|
|
||||||
|
QTestEventList keys;
|
||||||
|
keys.addKeyClick(key, modifier);
|
||||||
|
|
||||||
|
const auto modifierName = modifierToName(modifier);
|
||||||
|
if (modifierName.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const int steps = (modifier & Qt::ControlModifier ? 10 : 1)
|
||||||
|
* (up ? 1 : -1);
|
||||||
|
|
||||||
|
const int expectedValue = startValue + steps;
|
||||||
|
|
||||||
|
QTest::addRow("%sWith%sKeyboardModifier",
|
||||||
|
up ? "up" : "down",
|
||||||
|
modifierName.latin1())
|
||||||
|
<< startValue
|
||||||
|
<< keys
|
||||||
|
<< expectedValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QSpinBox::stepModifierKeys()
|
||||||
|
{
|
||||||
|
QFETCH(int, startValue);
|
||||||
|
QFETCH(QTestEventList, keys);
|
||||||
|
QFETCH(int, expectedValue);
|
||||||
|
|
||||||
|
QSpinBox spin(0);
|
||||||
|
spin.setValue(startValue);
|
||||||
|
spin.show();
|
||||||
|
QVERIFY(QTest::qWaitForWindowActive(&spin));
|
||||||
|
|
||||||
|
QCOMPARE(spin.value(), startValue);
|
||||||
|
keys.simulate(&spin);
|
||||||
|
QCOMPARE(spin.value(), expectedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QSpinBox::stepModifierButtons_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QStyle::SubControl>("subControl");
|
||||||
|
QTest::addColumn<Qt::KeyboardModifiers>("modifiers");
|
||||||
|
QTest::addColumn<int>("startValue");
|
||||||
|
QTest::addColumn<int>("expectedValue");
|
||||||
|
|
||||||
|
const auto subControls = {QStyle::SC_SpinBoxUp, QStyle::SC_SpinBoxDown};
|
||||||
|
|
||||||
|
const auto modifierList = {Qt::NoModifier,
|
||||||
|
Qt::ControlModifier,
|
||||||
|
Qt::ShiftModifier};
|
||||||
|
|
||||||
|
for (auto subControl : subControls) {
|
||||||
|
|
||||||
|
const bool up = subControl == QStyle::SC_SpinBoxUp;
|
||||||
|
Q_ASSERT(up || subControl == QStyle::SC_SpinBoxDown);
|
||||||
|
|
||||||
|
const int startValue = up ? 0 : 10;
|
||||||
|
|
||||||
|
for (auto modifier : modifierList) {
|
||||||
|
|
||||||
|
const Qt::KeyboardModifiers modifiers(modifier);
|
||||||
|
|
||||||
|
const auto modifierName = modifierToName(modifier);
|
||||||
|
if (modifierName.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const int steps = (modifier & Qt::ControlModifier ? 10 : 1)
|
||||||
|
* (up ? 1 : -1);
|
||||||
|
|
||||||
|
const int expectedValue = startValue + steps;
|
||||||
|
|
||||||
|
QTest::addRow("%sWith%sKeyboardModifier",
|
||||||
|
up ? "up" : "down",
|
||||||
|
modifierName.latin1())
|
||||||
|
<< subControl
|
||||||
|
<< modifiers
|
||||||
|
<< startValue
|
||||||
|
<< expectedValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QSpinBox::stepModifierButtons()
|
||||||
|
{
|
||||||
|
QFETCH(QStyle::SubControl, subControl);
|
||||||
|
QFETCH(Qt::KeyboardModifiers, modifiers);
|
||||||
|
QFETCH(int, startValue);
|
||||||
|
QFETCH(int, expectedValue);
|
||||||
|
|
||||||
|
SpinBox spin(0);
|
||||||
|
spin.setRange(-20, 20);
|
||||||
|
spin.setValue(startValue);
|
||||||
|
spin.show();
|
||||||
|
QVERIFY(QTest::qWaitForWindowActive(&spin));
|
||||||
|
|
||||||
|
QStyleOptionSpinBox spinBoxStyleOption;
|
||||||
|
spin.initStyleOption(&spinBoxStyleOption);
|
||||||
|
|
||||||
|
const QRect buttonRect = spin.style()->subControlRect(
|
||||||
|
QStyle::CC_SpinBox, &spinBoxStyleOption, subControl, &spin);
|
||||||
|
|
||||||
|
QCOMPARE(spin.value(), startValue);
|
||||||
|
QTest::mouseClick(&spin, Qt::LeftButton, modifiers, buttonRect.center());
|
||||||
|
QCOMPARE(spin.value(), expectedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QSpinBox::stepModifierPressAndHold_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QStyle::SubControl>("subControl");
|
||||||
|
QTest::addColumn<Qt::KeyboardModifiers>("modifiers");
|
||||||
|
QTest::addColumn<int>("expectedStepModifier");
|
||||||
|
|
||||||
|
const auto subControls = {QStyle::SC_SpinBoxUp, QStyle::SC_SpinBoxDown};
|
||||||
|
|
||||||
|
const auto modifierList = {Qt::NoModifier,
|
||||||
|
Qt::ControlModifier,
|
||||||
|
Qt::ShiftModifier};
|
||||||
|
|
||||||
|
for (auto subControl : subControls) {
|
||||||
|
|
||||||
|
const bool up = subControl == QStyle::SC_SpinBoxUp;
|
||||||
|
Q_ASSERT(up || subControl == QStyle::SC_SpinBoxDown);
|
||||||
|
|
||||||
|
for (auto modifier : modifierList) {
|
||||||
|
|
||||||
|
const Qt::KeyboardModifiers modifiers(modifier);
|
||||||
|
|
||||||
|
const auto modifierName = modifierToName(modifier);
|
||||||
|
if (modifierName.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const int steps = (modifier & Qt::ControlModifier ? 10 : 1)
|
||||||
|
* (up ? 1 : -1);
|
||||||
|
|
||||||
|
QTest::addRow("%sWith%sKeyboardModifier",
|
||||||
|
up ? "up" : "down",
|
||||||
|
modifierName.latin1())
|
||||||
|
<< subControl
|
||||||
|
<< modifiers
|
||||||
|
<< steps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QSpinBox::stepModifierPressAndHold()
|
||||||
|
{
|
||||||
|
QFETCH(QStyle::SubControl, subControl);
|
||||||
|
QFETCH(Qt::KeyboardModifiers, modifiers);
|
||||||
|
QFETCH(int, expectedStepModifier);
|
||||||
|
|
||||||
|
SpinBox spin(0);
|
||||||
|
QScopedPointer<PressAndHoldStyle, QScopedPointerDeleteLater> pressAndHoldStyle(
|
||||||
|
new PressAndHoldStyle);
|
||||||
|
spin.setStyle(pressAndHoldStyle.data());
|
||||||
|
spin.setRange(-100, 100);
|
||||||
|
spin.setValue(0);
|
||||||
|
|
||||||
|
QSignalSpy spy(&spin, QOverload<int>::of(&SpinBox::valueChanged));
|
||||||
|
|
||||||
|
spin.show();
|
||||||
|
QVERIFY(QTest::qWaitForWindowActive(&spin));
|
||||||
|
|
||||||
|
QStyleOptionSpinBox spinBoxStyleOption;
|
||||||
|
spin.initStyleOption(&spinBoxStyleOption);
|
||||||
|
|
||||||
|
const QRect buttonRect = spin.style()->subControlRect(
|
||||||
|
QStyle::CC_SpinBox, &spinBoxStyleOption, subControl, &spin);
|
||||||
|
|
||||||
|
QTest::mousePress(&spin, Qt::LeftButton, modifiers, buttonRect.center());
|
||||||
|
QTRY_VERIFY(spy.length() >= 3);
|
||||||
|
QTest::mouseRelease(&spin, Qt::LeftButton, modifiers, buttonRect.center());
|
||||||
|
|
||||||
|
const auto value = spy.last().at(0);
|
||||||
|
QVERIFY(value.type() == QVariant::Int);
|
||||||
|
QCOMPARE(value.toInt(), spy.length() * expectedStepModifier);
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_MAIN(tst_QSpinBox)
|
QTEST_MAIN(tst_QSpinBox)
|
||||||
#include "tst_qspinbox.moc"
|
#include "tst_qspinbox.moc"
|
||||||
|
Loading…
Reference in New Issue
Block a user