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
|
||||
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
|
||||
determine whether stepping up/down is allowed at any point. This
|
||||
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);
|
||||
if (!(stepEnabled() & (up ? StepUpEnabled : StepDownEnabled)))
|
||||
return;
|
||||
if (!isPgUpOrDown && (event->modifiers() & d->stepModifier))
|
||||
steps *= 10;
|
||||
if (!up)
|
||||
steps *= -1;
|
||||
if (style()->styleHint(QStyle::SH_SpinBox_AnimateButton, 0, this)) {
|
||||
@ -1141,11 +1149,24 @@ void QAbstractSpinBox::keyReleaseEvent(QKeyEvent *event)
|
||||
void QAbstractSpinBox::wheelEvent(QWheelEvent *event)
|
||||
{
|
||||
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();
|
||||
#endif
|
||||
const int steps = d->wheelDeltaRemainder / 120;
|
||||
d->wheelDeltaRemainder -= steps * 120;
|
||||
if (stepEnabled() & (steps > 0 ? StepUpEnabled : StepDownEnabled))
|
||||
stepBy(event->modifiers() & Qt::ControlModifier ? steps * 10 : steps);
|
||||
stepBy(event->modifiers() & d->stepModifier ? steps * 10 : steps);
|
||||
event->accept();
|
||||
}
|
||||
#endif
|
||||
@ -1245,18 +1266,19 @@ void QAbstractSpinBox::timerEvent(QTimerEvent *event)
|
||||
}
|
||||
|
||||
if (doStep) {
|
||||
const bool increaseStepRate = QGuiApplication::keyboardModifiers() & d->stepModifier;
|
||||
const StepEnabled st = stepEnabled();
|
||||
if (d->buttonState & Up) {
|
||||
if (!(st & StepUpEnabled)) {
|
||||
d->reset();
|
||||
} else {
|
||||
stepBy(1);
|
||||
stepBy(increaseStepRate ? 10 : 1);
|
||||
}
|
||||
} else if (d->buttonState & Down) {
|
||||
if (!(st & StepDownEnabled)) {
|
||||
d->reset();
|
||||
} else {
|
||||
stepBy(-1);
|
||||
stepBy(increaseStepRate ? -10 : -1);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@ -1384,8 +1406,9 @@ QAbstractSpinBoxPrivate::QAbstractSpinBoxPrivate()
|
||||
cachedState(QValidator::Invalid), pendingEmit(false), readOnly(false), wrapping(false),
|
||||
ignoreCursorPositionChanged(false), frame(true), accelerate(false), keyboardTracking(true),
|
||||
cleared(false), ignoreUpdateEdit(false), correctionMode(QAbstractSpinBox::CorrectToPreviousValue),
|
||||
acceleration(0), hoverControl(QStyle::SC_None), buttonSymbols(QAbstractSpinBox::UpDownArrows), validator(0),
|
||||
showGroupSeparator(0), wheelDeltaRemainder(0)
|
||||
stepModifier(Qt::ControlModifier), acceleration(0), hoverControl(QStyle::SC_None),
|
||||
buttonSymbols(QAbstractSpinBox::UpDownArrows), validator(0), showGroupSeparator(0),
|
||||
wheelDeltaRemainder(0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -1636,7 +1659,10 @@ void QAbstractSpinBoxPrivate::updateState(bool up, bool fromKeyboard /* = false
|
||||
: QAbstractSpinBox::StepDownEnabled))) {
|
||||
spinClickThresholdTimerId = q->startTimer(spinClickThresholdTimerInterval);
|
||||
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
|
||||
QAccessibleValueChangeEvent event(q, value);
|
||||
QAccessible::updateAccessibility(&event);
|
||||
|
@ -146,6 +146,7 @@ public:
|
||||
uint ignoreUpdateEdit : 1;
|
||||
QAbstractSpinBox::CorrectionMode correctionMode;
|
||||
QAbstractSpinBox::StepType stepType = QAbstractSpinBox::StepType::DefaultStepType;
|
||||
Qt::KeyboardModifier stepModifier = Qt::ControlModifier;
|
||||
int acceleration;
|
||||
QStyle::SubControl hoverControl;
|
||||
QRect hoverRect;
|
||||
|
@ -70,6 +70,7 @@
|
||||
#include <QSignalSpy>
|
||||
#include <QTestEventList>
|
||||
#include <QDateEdit>
|
||||
#include <QProxyStyle>
|
||||
|
||||
#include <private/qdatetimeedit_p.h>
|
||||
|
||||
@ -93,6 +94,26 @@ public:
|
||||
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
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -205,9 +226,8 @@ private slots:
|
||||
void reverseTest();
|
||||
|
||||
void ddMMMMyyyy();
|
||||
#if QT_CONFIG(wheelevent)
|
||||
void wheelEvent_data();
|
||||
void wheelEvent();
|
||||
#endif
|
||||
|
||||
void specialValueCornerCase();
|
||||
void cursorPositionOnInit();
|
||||
@ -245,14 +265,99 @@ private slots:
|
||||
void dateEditCorrectSectionSize_data();
|
||||
void dateEditCorrectSectionSize();
|
||||
#endif
|
||||
|
||||
void stepModifierKeys_data();
|
||||
void stepModifierKeys();
|
||||
|
||||
void stepModifierButtons_data();
|
||||
void stepModifierButtons();
|
||||
|
||||
void stepModifierPressAndHold_data();
|
||||
void stepModifierPressAndHold();
|
||||
private:
|
||||
EditorDateEdit* testWidget;
|
||||
QWidget *testFocusWidget;
|
||||
};
|
||||
|
||||
typedef QList<QDate> DateList;
|
||||
typedef QList<QTime> TimeList;
|
||||
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
|
||||
void tst_QDateTimeEdit::getSetCheck()
|
||||
{
|
||||
@ -3000,20 +3105,154 @@ void tst_QDateTimeEdit::ddMMMMyyyy()
|
||||
QCOMPARE(testWidget->lineEdit()->text(), "01." + QDate::longMonthName(1) + ".200");
|
||||
}
|
||||
|
||||
void tst_QDateTimeEdit::wheelEvent_data()
|
||||
{
|
||||
#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()
|
||||
{
|
||||
testWidget->setDisplayFormat("dddd/MM");
|
||||
testWidget->setDate(QDate(2000, 2, 21));
|
||||
testWidget->setCurrentSection(QDateTimeEdit::DaySection);
|
||||
QWheelEvent w(testWidget->lineEdit()->geometry().center(), 120, 0, 0);
|
||||
qApp->sendEvent(testWidget, &w);
|
||||
QCOMPARE(testWidget->date(), QDate(2000, 2, 22));
|
||||
testWidget->setCurrentSection(QDateTimeEdit::MonthSection);
|
||||
qApp->sendEvent(testWidget, &w);
|
||||
QCOMPARE(testWidget->date(), QDate(2000, 3, 22));
|
||||
}
|
||||
#if QT_CONFIG(wheelevent)
|
||||
QFETCH(QPoint, angleDelta);
|
||||
QFETCH(int, qt4Delta);
|
||||
QFETCH(Qt::KeyboardModifiers, modifiers);
|
||||
QFETCH(Qt::MouseEventSource, source);
|
||||
QFETCH(QDateTimeEdit::Section, section);
|
||||
QFETCH(QDate, startDate);
|
||||
QFETCH(DateList, expectedDates);
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
void tst_QDateTimeEdit::specialValueCornerCase()
|
||||
{
|
||||
@ -3735,5 +3974,238 @@ void tst_QDateTimeEdit::dateEditCorrectSectionSize()
|
||||
}
|
||||
#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)
|
||||
#include "tst_qdatetimeedit.moc"
|
||||
|
@ -41,6 +41,10 @@
|
||||
#include <qlineedit.h>
|
||||
#include <qdebug.h>
|
||||
|
||||
#include <QStyleOptionSpinBox>
|
||||
#include <QStyle>
|
||||
#include <QProxyStyle>
|
||||
|
||||
class DoubleSpinBox : public QDoubleSpinBox
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -60,6 +64,16 @@ public:
|
||||
{
|
||||
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(); }
|
||||
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
|
||||
{
|
||||
@ -138,6 +172,17 @@ private slots:
|
||||
|
||||
void adaptiveDecimalStep();
|
||||
|
||||
void wheelEvents_data();
|
||||
void wheelEvents();
|
||||
|
||||
void stepModifierKeys_data();
|
||||
void stepModifierKeys();
|
||||
|
||||
void stepModifierButtons_data();
|
||||
void stepModifierButtons();
|
||||
|
||||
void stepModifierPressAndHold_data();
|
||||
void stepModifierPressAndHold();
|
||||
public slots:
|
||||
void valueChangedHelper(const QString &);
|
||||
void valueChangedHelper(double);
|
||||
@ -152,6 +197,24 @@ typedef QList<double> DoubleList;
|
||||
Q_DECLARE_METATYPE(QLocale::Language)
|
||||
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()
|
||||
|
||||
{
|
||||
@ -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)
|
||||
#include "tst_qdoublespinbox.moc"
|
||||
|
@ -50,6 +50,9 @@
|
||||
#include <QKeySequence>
|
||||
#include <QStackedWidget>
|
||||
#include <QDebug>
|
||||
#include <QStyleOptionSpinBox>
|
||||
#include <QStyle>
|
||||
#include <QProxyStyle>
|
||||
|
||||
class SpinBox : public QSpinBox
|
||||
{
|
||||
@ -75,10 +78,34 @@ public:
|
||||
QSpinBox::wheelEvent(event);
|
||||
}
|
||||
#endif
|
||||
void initStyleOption(QStyleOptionSpinBox *option) const
|
||||
{
|
||||
QSpinBox::initStyleOption(option);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -143,10 +170,19 @@ private slots:
|
||||
void setGroupSeparatorShown_data();
|
||||
void setGroupSeparatorShown();
|
||||
|
||||
void wheelEvents_data();
|
||||
void wheelEvents();
|
||||
|
||||
void adaptiveDecimalStep();
|
||||
|
||||
void stepModifierKeys_data();
|
||||
void stepModifierKeys();
|
||||
|
||||
void stepModifierButtons_data();
|
||||
void stepModifierButtons();
|
||||
|
||||
void stepModifierPressAndHold_data();
|
||||
void stepModifierPressAndHold();
|
||||
public slots:
|
||||
void valueChangedHelper(const QString &);
|
||||
void valueChangedHelper(int);
|
||||
@ -160,6 +196,24 @@ typedef QList<int> IntList;
|
||||
Q_DECLARE_METATYPE(QLocale::Language)
|
||||
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
|
||||
void tst_QSpinBox::getSetCheck()
|
||||
{
|
||||
@ -1217,27 +1271,132 @@ void tst_QSpinBox::setGroupSeparatorShown()
|
||||
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()
|
||||
{
|
||||
#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.setRange(-20, 20);
|
||||
spinBox.setValue(0);
|
||||
spinBox.setValue(start);
|
||||
|
||||
QWheelEvent wheelUp(QPointF(), QPointF(), QPoint(), QPoint(0, 120), 120, Qt::Vertical, Qt::NoButton, Qt::NoModifier);
|
||||
spinBox.wheelEvent(&wheelUp);
|
||||
QCOMPARE(spinBox.value(), 1);
|
||||
|
||||
QWheelEvent wheelDown(QPointF(), QPointF(), QPoint(), QPoint(0, -120), -120, Qt::Vertical, Qt::NoButton, Qt::NoModifier);
|
||||
spinBox.wheelEvent(&wheelDown);
|
||||
spinBox.wheelEvent(&wheelDown);
|
||||
QCOMPARE(spinBox.value(), -1);
|
||||
|
||||
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);
|
||||
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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
#include "tst_qspinbox.moc"
|
||||
|
Loading…
Reference in New Issue
Block a user