From e40f23f098a1e79c22df611b6312317582b95a3a Mon Sep 17 00:00:00 2001 From: Nathan Collins Date: Fri, 18 May 2018 16:27:43 +0100 Subject: Add QStyle::SH_SpinBox_StepModifier style hint This patch allows the developer to pick which keyboard modifier increases the number of steps a QAbstractSpinBox takes when the user interacts with it. The modifier can be either Qt::ControlModifier (default), Qt::ShiftModifier or Qt::NoModifier. Qt::NoModifier disables the step modifier. Note that on macOS, Control corresponds to the Command key. Holding the modifier increases the step rate when: - scrolling; - pressing the up/down keys; - pressing the spin box up/down buttons. [ChangeLog][QtWidgets][QStyle] QStyle::SH_SpinBox_StepModifier allows the developer to pick which keyboard modifier increases the number of steps a QAbstractSpinBox takes for the following interactions: scrolling, up/down keyboard keys and the spin box buttons. The Qt::ShiftModifier can now be used, or the feature can be disabled using Qt::NoModifier. Previously, only Qt::ControlModifier could be used as the modifier. Change-Id: Ib5518127e86a8f67798a9a1d6e860c6e35896e6f Reviewed-by: Richard Moe Gustavsen --- src/widgets/styles/qcommonstyle.cpp | 3 + src/widgets/styles/qstyle.cpp | 7 + src/widgets/styles/qstyle.h | 1 + src/widgets/widgets/qabstractspinbox.cpp | 5 + .../widgets/qdatetimeedit/tst_qdatetimeedit.cpp | 327 ++++++++++++++------- .../widgets/qdoublespinbox/tst_qdoublespinbox.cpp | 282 ++++++++++++------ .../auto/widgets/widgets/qspinbox/tst_qspinbox.cpp | 279 ++++++++++++------ 7 files changed, 629 insertions(+), 275 deletions(-) diff --git a/src/widgets/styles/qcommonstyle.cpp b/src/widgets/styles/qcommonstyle.cpp index 4c01807ca7..544cc5ef95 100644 --- a/src/widgets/styles/qcommonstyle.cpp +++ b/src/widgets/styles/qcommonstyle.cpp @@ -5306,6 +5306,9 @@ int QCommonStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget case SH_SpinBox_ButtonsInsideFrame: ret = true; break; + case SH_SpinBox_StepModifier: + ret = Qt::ControlModifier; + break; default: ret = 0; break; diff --git a/src/widgets/styles/qstyle.cpp b/src/widgets/styles/qstyle.cpp index 73a6554f1a..8006be8c27 100644 --- a/src/widgets/styles/qstyle.cpp +++ b/src/widgets/styles/qstyle.cpp @@ -2001,6 +2001,13 @@ void QStyle::drawItemPixmap(QPainter *painter, const QRect &rect, int alignment, Determnines if the spin box buttons are inside the line edit frame. This enum value has been introduced in Qt 5.11. + \value SH_SpinBox_StepModifier + Determines which Qt::KeyboardModifier increases the step rate of + QAbstractSpinBox. Possible values are Qt::NoModifier, + Qt::ControlModifier (default) or Qt::ShiftModifier. Qt::NoModifier + disables this feature. + This enum value has been introduced in Qt 5.12. + \sa styleHint() */ diff --git a/src/widgets/styles/qstyle.h b/src/widgets/styles/qstyle.h index cef569d514..9192dae864 100644 --- a/src/widgets/styles/qstyle.h +++ b/src/widgets/styles/qstyle.h @@ -741,6 +741,7 @@ public: SH_Widget_Animation_Duration, SH_ComboBox_AllowWheelScrolling, SH_SpinBox_ButtonsInsideFrame, + SH_SpinBox_StepModifier, // Add new style hint values here SH_CustomBase = 0xf0000000 diff --git a/src/widgets/widgets/qabstractspinbox.cpp b/src/widgets/widgets/qabstractspinbox.cpp index 0459eadfff..822d896ee6 100644 --- a/src/widgets/widgets/qabstractspinbox.cpp +++ b/src/widgets/widgets/qabstractspinbox.cpp @@ -106,6 +106,10 @@ QT_BEGIN_NAMESPACE the spinbox buttons. Note that on macOS, Control corresponds to the Command key. + Since Qt 5.12, QStyle::SH_SpinBox_StepModifier can be used to select + which Qt::KeyboardModifier increases the step rate. Qt::NoModifier + disables this feature. + 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. @@ -840,6 +844,7 @@ void QAbstractSpinBox::changeEvent(QEvent *event) style()->styleHint(QStyle::SH_SpinBox_ClickAutoRepeatThreshold, 0, this); if (d->edit) d->edit->setFrame(!style()->styleHint(QStyle::SH_SpinBox_ButtonsInsideFrame, nullptr, this)); + d->stepModifier = static_cast(style()->styleHint(QStyle::SH_SpinBox_StepModifier, nullptr, this)); d->reset(); d->updateEditFieldGeometry(); break; diff --git a/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp b/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp index 0fe5b48341..3c7a297f64 100644 --- a/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp +++ b/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp @@ -114,6 +114,26 @@ public: } }; +class StepModifierStyle : 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_StepModifier: + return stepModifier; + default: + return QProxyStyle::styleHint(hint, option, widget, returnData); + } + } + + Qt::KeyboardModifier stepModifier = Qt::ControlModifier; +}; + class tst_QDateTimeEdit : public QObject { Q_OBJECT @@ -295,6 +315,12 @@ static QLatin1String modifierToName(Qt::KeyboardModifier modifier) case Qt::ShiftModifier: return QLatin1Literal("Shift"); break; + case Qt::AltModifier: + return QLatin1Literal("Alt"); + break; + case Qt::MetaModifier: + return QLatin1Literal("Meta"); + break; default: qFatal("Unexpected keyboard modifier"); return QLatin1String(); @@ -3110,6 +3136,7 @@ void tst_QDateTimeEdit::wheelEvent_data() #if QT_CONFIG(wheelevent) QTest::addColumn("angleDelta"); QTest::addColumn("qt4Delta"); + QTest::addColumn("stepModifier"); QTest::addColumn("modifiers"); QTest::addColumn("source"); QTest::addColumn("section"); @@ -3121,8 +3148,14 @@ void tst_QDateTimeEdit::wheelEvent_data() const auto directions = {true, false}; const auto modifierList = {Qt::NoModifier, + Qt::ShiftModifier, Qt::ControlModifier, - Qt::ShiftModifier}; + Qt::AltModifier, + Qt::MetaModifier}; + + const auto validStepModifierList = {Qt::NoModifier, + Qt::ControlModifier, + Qt::ShiftModifier}; const auto sources = {Qt::MouseEventNotSynthesized, Qt::MouseEventSynthesizedBySystem, @@ -3148,73 +3181,82 @@ void tst_QDateTimeEdit::wheelEvent_data() if (modifierName.isEmpty()) continue; - const int steps = (modifier & Qt::ControlModifier ? 10 : 1) - * (up ? 1 : -1); + for (auto stepModifier : validStepModifierList) { + + const auto stepModifierName = modifierToName(stepModifier); + if (stepModifierName.isEmpty()) + continue; + + const int steps = (modifier & stepModifier ? 10 : 1) + * (up ? 1 : -1); - for (auto source : sources) { + 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 }; - } + 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); + 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()) + 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; - - 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; + } + + 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%s%sWith%sKeyboardModifier%s", + fraction ? "half" : "full", + up ? "Up" : "Down", + stepModifierName.latin1(), + sectionName.latin1(), + modifierName.latin1(), + sourceName.latin1()) + << angleDelta + << units + << static_cast(stepModifier) + << modifiers + << source + << section + << startDate + << expectedDates; + } } } } @@ -3230,6 +3272,7 @@ void tst_QDateTimeEdit::wheelEvent() #if QT_CONFIG(wheelevent) QFETCH(QPoint, angleDelta); QFETCH(int, qt4Delta); + QFETCH(int, stepModifier); QFETCH(Qt::KeyboardModifiers, modifiers); QFETCH(Qt::MouseEventSource, source); QFETCH(QDateTimeEdit::Section, section); @@ -3240,6 +3283,11 @@ void tst_QDateTimeEdit::wheelEvent() edit.setDate(startDate); edit.setCurrentSection(section); + QScopedPointer style( + new StepModifierStyle); + style->stepModifier = static_cast(stepModifier); + edit.setStyle(style.data()); + QWheelEvent event(QPointF(), QPointF(), QPoint(), angleDelta, qt4Delta, Qt::Vertical, Qt::NoButton, modifiers, Qt::NoScrollPhase, source); @@ -3977,6 +4025,7 @@ void tst_QDateTimeEdit::dateEditCorrectSectionSize() void tst_QDateTimeEdit::stepModifierKeys_data() { QTest::addColumn("startDate"); + QTest::addColumn("stepModifier"); QTest::addColumn("section"); QTest::addColumn("keys"); QTest::addColumn("expectedDate"); @@ -3984,8 +4033,14 @@ void tst_QDateTimeEdit::stepModifierKeys_data() const auto keyList = {Qt::Key_Up, Qt::Key_Down}; const auto modifierList = {Qt::NoModifier, - Qt::ControlModifier, - Qt::ShiftModifier}; + Qt::ShiftModifier, + Qt::ControlModifier, + Qt::AltModifier, + Qt::MetaModifier}; + + const auto validStepModifierList = {Qt::NoModifier, + Qt::ControlModifier, + Qt::ShiftModifier}; const auto sections = {QDateTimeEdit::DaySection, QDateTimeEdit::MonthSection, @@ -4007,25 +4062,34 @@ void tst_QDateTimeEdit::stepModifierKeys_data() if (modifierName.isEmpty()) continue; - const int steps = (modifier & Qt::ControlModifier ? 10 : 1) - * (up ? 1 : -1); + for (auto stepModifier : validStepModifierList) { - for (const auto section : sections) { - - const auto expectedDate = stepDate(startDate, section, steps); - if (!expectedDate.isValid()) + const auto stepModifierName = modifierToName(stepModifier); + if (stepModifierName.isEmpty()) continue; - const auto sectionName = sectionToName(section); + const int steps = (modifier & stepModifier ? 10 : 1) + * (up ? 1 : -1); - QTest::addRow("%s%sWith%sKeyboardModifier", - up ? "up" : "down", - sectionName.latin1(), - modifierName.latin1()) - << startDate - << section - << keys - << expectedDate; + for (const auto section : sections) { + + const auto expectedDate = stepDate(startDate, section, steps); + if (!expectedDate.isValid()) + continue; + + const auto sectionName = sectionToName(section); + + QTest::addRow("%s%s%sWith%sKeyboardModifier", + up ? "up" : "down", + stepModifierName.latin1(), + sectionName.latin1(), + modifierName.latin1()) + << startDate + << static_cast(stepModifier) + << section + << keys + << expectedDate; + } } } } @@ -4034,6 +4098,7 @@ void tst_QDateTimeEdit::stepModifierKeys_data() void tst_QDateTimeEdit::stepModifierKeys() { QFETCH(QDate, startDate); + QFETCH(int, stepModifier); QFETCH(QDateTimeEdit::Section, section); QFETCH(QTestEventList, keys); QFETCH(QDate, expectedDate); @@ -4044,6 +4109,11 @@ void tst_QDateTimeEdit::stepModifierKeys() QVERIFY(QTest::qWaitForWindowActive(&edit)); edit.setCurrentSection(section); + QScopedPointer style( + new StepModifierStyle); + style->stepModifier = static_cast(stepModifier); + edit.setStyle(style.data()); + QCOMPARE(edit.date(), startDate); keys.simulate(&edit); QCOMPARE(edit.date(), expectedDate); @@ -4052,6 +4122,7 @@ void tst_QDateTimeEdit::stepModifierKeys() void tst_QDateTimeEdit::stepModifierButtons_data() { QTest::addColumn("subControl"); + QTest::addColumn("stepModifier"); QTest::addColumn("modifiers"); QTest::addColumn("section"); QTest::addColumn("startTime"); @@ -4060,8 +4131,14 @@ void tst_QDateTimeEdit::stepModifierButtons_data() const auto subControls = {QStyle::SC_SpinBoxUp, QStyle::SC_SpinBoxDown}; const auto modifierList = {Qt::NoModifier, - Qt::ControlModifier, - Qt::ShiftModifier}; + Qt::ShiftModifier, + Qt::ControlModifier, + Qt::AltModifier, + Qt::MetaModifier}; + + const auto validStepModifierList = {Qt::NoModifier, + Qt::ControlModifier, + Qt::ShiftModifier}; const auto sections = {QDateTimeEdit::SecondSection, QDateTimeEdit::MinuteSection, @@ -4082,26 +4159,35 @@ void tst_QDateTimeEdit::stepModifierButtons_data() if (modifierName.isEmpty()) continue; - const int steps = (modifier & Qt::ControlModifier ? 10 : 1) - * (up ? 1 : -1); - - for (const auto section : sections) { + for (auto stepModifier : validStepModifierList) { - const auto expectedTime = stepTime(startTime, section, steps); - if (!expectedTime.isValid()) + const auto stepModifierName = modifierToName(stepModifier); + if (stepModifierName.isEmpty()) continue; - const auto sectionName = sectionToName(section); + const int steps = (modifier & stepModifier ? 10 : 1) + * (up ? 1 : -1); - QTest::addRow("%s%sWith%sKeyboardModifier", - up ? "up" : "down", - sectionName.latin1(), - modifierName.latin1()) - << subControl - << modifiers - << section - << startTime - << expectedTime; + for (const auto section : sections) { + + const auto expectedTime = stepTime(startTime, section, steps); + if (!expectedTime.isValid()) + continue; + + const auto sectionName = sectionToName(section); + + QTest::addRow("%s%s%sWith%sKeyboardModifier", + up ? "up" : "down", + stepModifierName.latin1(), + sectionName.latin1(), + modifierName.latin1()) + << subControl + << static_cast(stepModifier) + << modifiers + << section + << startTime + << expectedTime; + } } } } @@ -4110,6 +4196,7 @@ void tst_QDateTimeEdit::stepModifierButtons_data() void tst_QDateTimeEdit::stepModifierButtons() { QFETCH(QStyle::SubControl, subControl); + QFETCH(int, stepModifier); QFETCH(Qt::KeyboardModifiers, modifiers); QFETCH(QDateTimeEdit::Section, section); QFETCH(QTime, startTime); @@ -4121,6 +4208,11 @@ void tst_QDateTimeEdit::stepModifierButtons() QVERIFY(QTest::qWaitForWindowActive(&edit)); edit.setCurrentSection(section); + QScopedPointer style( + new StepModifierStyle); + style->stepModifier = static_cast(stepModifier); + edit.setStyle(style.data()); + QStyleOptionSpinBox spinBoxStyleOption; edit.initStyleOption(&spinBoxStyleOption); @@ -4135,14 +4227,21 @@ void tst_QDateTimeEdit::stepModifierButtons() void tst_QDateTimeEdit::stepModifierPressAndHold_data() { QTest::addColumn("subControl"); + QTest::addColumn("stepModifier"); QTest::addColumn("modifiers"); QTest::addColumn("expectedStepModifier"); const auto subControls = {QStyle::SC_SpinBoxUp, QStyle::SC_SpinBoxDown}; const auto modifierList = {Qt::NoModifier, + Qt::ShiftModifier, Qt::ControlModifier, - Qt::ShiftModifier}; + Qt::AltModifier, + Qt::MetaModifier}; + + const auto validStepModifierList = {Qt::NoModifier, + Qt::ControlModifier, + Qt::ShiftModifier}; for (auto subControl : subControls) { @@ -4157,15 +4256,24 @@ void tst_QDateTimeEdit::stepModifierPressAndHold_data() if (modifierName.isEmpty()) continue; - const int steps = (modifier & Qt::ControlModifier ? 10 : 1) - * (up ? 1 : -1); + for (auto stepModifier : validStepModifierList) { + + const auto stepModifierName = modifierToName(stepModifier); + if (stepModifierName.isEmpty()) + continue; - QTest::addRow("%sWith%sKeyboardModifier", - up ? "up" : "down", - modifierName.latin1()) - << subControl - << modifiers - << steps; + const int steps = (modifier & stepModifier ? 10 : 1) + * (up ? 1 : -1); + + QTest::addRow("%s%sWith%sKeyboardModifier", + up ? "up" : "down", + stepModifierName.latin1(), + modifierName.latin1()) + << subControl + << static_cast(stepModifier) + << modifiers + << steps; + } } } } @@ -4173,17 +4281,20 @@ void tst_QDateTimeEdit::stepModifierPressAndHold_data() void tst_QDateTimeEdit::stepModifierPressAndHold() { QFETCH(QStyle::SubControl, subControl); + QFETCH(int, stepModifier); QFETCH(Qt::KeyboardModifiers, modifiers); QFETCH(int, expectedStepModifier); const QDate startDate(2000, 1, 1); EditorDateEdit edit(0); - QScopedPointer pressAndHoldStyle( - new PressAndHoldStyle); - edit.setStyle(pressAndHoldStyle.data()); edit.setDate(startDate); + QScopedPointer stepModifierStyle( + new StepModifierStyle(new PressAndHoldStyle)); + stepModifierStyle->stepModifier = static_cast(stepModifier); + edit.setStyle(stepModifierStyle.data()); + QSignalSpy spy(&edit, &EditorDateEdit::dateChanged); edit.show(); diff --git a/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp b/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp index ee9446f00b..b1610c297d 100644 --- a/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp +++ b/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp @@ -103,6 +103,26 @@ public: } }; +class StepModifierStyle : 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_StepModifier: + return stepModifier; + default: + return QProxyStyle::styleHint(hint, option, widget, returnData); + } + } + + Qt::KeyboardModifier stepModifier = Qt::ControlModifier; +}; + class tst_QDoubleSpinBox : public QObject { @@ -209,6 +229,12 @@ static QLatin1String modifierToName(Qt::KeyboardModifier modifier) case Qt::ShiftModifier: return QLatin1Literal("Shift"); break; + case Qt::AltModifier: + return QLatin1Literal("Alt"); + break; + case Qt::MetaModifier: + return QLatin1Literal("Meta"); + break; default: qFatal("Unexpected keyboard modifier"); return QLatin1String(); @@ -1338,6 +1364,7 @@ void tst_QDoubleSpinBox::wheelEvents_data() #if QT_CONFIG(wheelevent) QTest::addColumn("angleDelta"); QTest::addColumn("qt4Delta"); + QTest::addColumn("stepModifier"); QTest::addColumn("modifier"); QTest::addColumn("source"); QTest::addColumn("start"); @@ -1348,8 +1375,14 @@ void tst_QDoubleSpinBox::wheelEvents_data() const auto directions = {true, false}; const auto modifierList = {Qt::NoModifier, + Qt::ShiftModifier, Qt::ControlModifier, - Qt::ShiftModifier}; + Qt::AltModifier, + Qt::MetaModifier}; + + const auto validStepModifierList = {Qt::NoModifier, + Qt::ControlModifier, + Qt::ShiftModifier}; const auto sources = {Qt::MouseEventNotSynthesized, Qt::MouseEventSynthesizedBySystem, @@ -1371,62 +1404,71 @@ void tst_QDoubleSpinBox::wheelEvents_data() if (modifierName.isEmpty()) continue; - const int steps = (modifier & Qt::ControlModifier ? 10 : 1) - * (up ? 1 : -1); + for (auto stepModifier : validStepModifierList) { - for (auto source : sources) { + const auto stepModifierName = modifierToName(stepModifier); + if (stepModifierName.isEmpty()) + continue; + + const int steps = (modifier & stepModifier ? 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 }; - } + 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); + 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; + 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%s%sWith%sKeyboardModifier%s", + fraction ? "half" : "full", + up ? "Up" : "Down", + stepModifierName.latin1(), + modifierName.latin1(), + sourceName.latin1()) + << angleDelta + << units + << static_cast(stepModifier) + << modifiers + << source + << startValue + << expectedValues; } - - 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; } } } @@ -1441,6 +1483,7 @@ void tst_QDoubleSpinBox::wheelEvents() #if QT_CONFIG(wheelevent) QFETCH(QPoint, angleDelta); QFETCH(int, qt4Delta); + QFETCH(int, stepModifier); QFETCH(Qt::KeyboardModifiers, modifier); QFETCH(Qt::MouseEventSource, source); QFETCH(double, start); @@ -1450,6 +1493,11 @@ void tst_QDoubleSpinBox::wheelEvents() spinBox.setRange(-20, 20); spinBox.setValue(start); + QScopedPointer style( + new StepModifierStyle); + style->stepModifier = static_cast(stepModifier); + spinBox.setStyle(style.data()); + QWheelEvent event(QPointF(), QPointF(), QPoint(), angleDelta, qt4Delta, Qt::Vertical, Qt::NoButton, modifier, Qt::NoScrollPhase, source); @@ -1465,14 +1513,22 @@ void tst_QDoubleSpinBox::wheelEvents() void tst_QDoubleSpinBox::stepModifierKeys_data() { QTest::addColumn("startValue"); + QTest::addColumn("stepModifier"); QTest::addColumn("keys"); QTest::addColumn("expectedValue"); const auto keyList = {Qt::Key_Up, Qt::Key_Down}; const auto modifierList = {Qt::NoModifier, + Qt::ShiftModifier, Qt::ControlModifier, - Qt::ShiftModifier}; + Qt::AltModifier, + Qt::MetaModifier}; + + const auto validStepModifierList = {Qt::NoModifier, + Qt::ControlModifier, + Qt::ShiftModifier}; + for (auto key : keyList) { @@ -1490,17 +1546,26 @@ void tst_QDoubleSpinBox::stepModifierKeys_data() if (modifierName.isEmpty()) continue; - const int steps = (modifier & Qt::ControlModifier ? 10 : 1) - * (up ? 1 : -1); + for (auto stepModifier : validStepModifierList) { + + const auto stepModifierName = modifierToName(stepModifier); + if (stepModifierName.isEmpty()) + continue; + + const int steps = (modifier & stepModifier ? 10 : 1) + * (up ? 1 : -1); - const double expectedValue = startValue + steps; + const double expectedValue = startValue + steps; - QTest::addRow("%sWith%sKeyboardModifier", - up ? "up" : "down", - modifierName.latin1()) - << startValue - << keys - << expectedValue; + QTest::addRow("%s%sWith%sKeyboardModifier", + up ? "up" : "down", + stepModifierName.latin1(), + modifierName.latin1()) + << startValue + << static_cast(stepModifier) + << keys + << expectedValue; + } } } } @@ -1508,11 +1573,18 @@ void tst_QDoubleSpinBox::stepModifierKeys_data() void tst_QDoubleSpinBox::stepModifierKeys() { QFETCH(double, startValue); + QFETCH(int, stepModifier); QFETCH(QTestEventList, keys); QFETCH(double, expectedValue); QDoubleSpinBox spin(0); spin.setValue(startValue); + + QScopedPointer style( + new StepModifierStyle); + style->stepModifier = static_cast(stepModifier); + spin.setStyle(style.data()); + spin.show(); QVERIFY(QTest::qWaitForWindowActive(&spin)); @@ -1524,6 +1596,7 @@ void tst_QDoubleSpinBox::stepModifierKeys() void tst_QDoubleSpinBox::stepModifierButtons_data() { QTest::addColumn("subControl"); + QTest::addColumn("stepModifier"); QTest::addColumn("modifiers"); QTest::addColumn("startValue"); QTest::addColumn("expectedValue"); @@ -1531,8 +1604,14 @@ void tst_QDoubleSpinBox::stepModifierButtons_data() const auto subControls = {QStyle::SC_SpinBoxUp, QStyle::SC_SpinBoxDown}; const auto modifierList = {Qt::NoModifier, - Qt::ControlModifier, - Qt::ShiftModifier}; + Qt::ShiftModifier, + Qt::ControlModifier, + Qt::AltModifier, + Qt::MetaModifier}; + + const auto validStepModifierList = {Qt::NoModifier, + Qt::ControlModifier, + Qt::ShiftModifier}; for (auto subControl : subControls) { @@ -1549,18 +1628,27 @@ void tst_QDoubleSpinBox::stepModifierButtons_data() if (modifierName.isEmpty()) continue; - const int steps = (modifier & Qt::ControlModifier ? 10 : 1) - * (up ? 1 : -1); + for (auto stepModifier : validStepModifierList) { - const double expectedValue = startValue + steps; + const auto stepModifierName = modifierToName(stepModifier); + if (stepModifierName.isEmpty()) + continue; - QTest::addRow("%sWith%sKeyboardModifier", - up ? "up" : "down", - modifierName.latin1()) - << subControl - << modifiers - << startValue - << expectedValue; + const int steps = (modifier & stepModifier ? 10 : 1) + * (up ? 1 : -1); + + const double expectedValue = startValue + steps; + + QTest::addRow("%s%sWith%sKeyboardModifier", + up ? "up" : "down", + stepModifierName.latin1(), + modifierName.latin1()) + << subControl + << static_cast(stepModifier) + << modifiers + << startValue + << expectedValue; + } } } } @@ -1568,6 +1656,7 @@ void tst_QDoubleSpinBox::stepModifierButtons_data() void tst_QDoubleSpinBox::stepModifierButtons() { QFETCH(QStyle::SubControl, subControl); + QFETCH(int, stepModifier); QFETCH(Qt::KeyboardModifiers, modifiers); QFETCH(double, startValue); QFETCH(double, expectedValue); @@ -1575,6 +1664,12 @@ void tst_QDoubleSpinBox::stepModifierButtons() DoubleSpinBox spin(0); spin.setRange(-20, 20); spin.setValue(startValue); + + QScopedPointer style( + new StepModifierStyle); + style->stepModifier = static_cast(stepModifier); + spin.setStyle(style.data()); + spin.show(); QVERIFY(QTest::qWaitForWindowActive(&spin)); @@ -1592,14 +1687,21 @@ void tst_QDoubleSpinBox::stepModifierButtons() void tst_QDoubleSpinBox::stepModifierPressAndHold_data() { QTest::addColumn("subControl"); + QTest::addColumn("stepModifier"); QTest::addColumn("modifiers"); QTest::addColumn("expectedStepModifier"); const auto subControls = {QStyle::SC_SpinBoxUp, QStyle::SC_SpinBoxDown}; const auto modifierList = {Qt::NoModifier, + Qt::ShiftModifier, Qt::ControlModifier, - Qt::ShiftModifier}; + Qt::AltModifier, + Qt::MetaModifier}; + + const auto validStepModifierList = {Qt::NoModifier, + Qt::ControlModifier, + Qt::ShiftModifier}; for (auto subControl : subControls) { @@ -1614,15 +1716,24 @@ void tst_QDoubleSpinBox::stepModifierPressAndHold_data() if (modifierName.isEmpty()) continue; - const int steps = (modifier & Qt::ControlModifier ? 10 : 1) - * (up ? 1 : -1); + for (auto stepModifier : validStepModifierList) { - QTest::addRow("%sWith%sKeyboardModifier", - up ? "up" : "down", - modifierName.latin1()) - << subControl - << modifiers - << steps; + const auto stepModifierName = modifierToName(stepModifier); + if (stepModifierName.isEmpty()) + continue; + + const int steps = (modifier & stepModifier ? 10 : 1) + * (up ? 1 : -1); + + QTest::addRow("%s%sWith%sKeyboardModifier", + up ? "up" : "down", + stepModifierName.latin1(), + modifierName.latin1()) + << subControl + << static_cast(stepModifier) + << modifiers + << steps; + } } } } @@ -1630,16 +1741,19 @@ void tst_QDoubleSpinBox::stepModifierPressAndHold_data() void tst_QDoubleSpinBox::stepModifierPressAndHold() { QFETCH(QStyle::SubControl, subControl); + QFETCH(int, stepModifier); QFETCH(Qt::KeyboardModifiers, modifiers); QFETCH(int, expectedStepModifier); DoubleSpinBox spin(0); - QScopedPointer pressAndHoldStyle( - new PressAndHoldStyle); - spin.setStyle(pressAndHoldStyle.data()); spin.setRange(-100.0, 100.0); spin.setValue(0.0); + QScopedPointer stepModifierStyle( + new StepModifierStyle(new PressAndHoldStyle)); + stepModifierStyle->stepModifier = static_cast(stepModifier); + spin.setStyle(stepModifierStyle.data()); + QSignalSpy spy(&spin, QOverload::of(&DoubleSpinBox::valueChanged)); spin.show(); diff --git a/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp b/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp index 877ccbdec2..22ca608e34 100644 --- a/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp +++ b/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp @@ -106,6 +106,26 @@ public: } }; +class StepModifierStyle : 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_StepModifier: + return stepModifier; + default: + return QProxyStyle::styleHint(hint, option, widget, returnData); + } + } + + Qt::KeyboardModifier stepModifier = Qt::ControlModifier; +}; + class tst_QSpinBox : public QObject { Q_OBJECT @@ -208,6 +228,12 @@ static QLatin1String modifierToName(Qt::KeyboardModifier modifier) case Qt::ShiftModifier: return QLatin1Literal("Shift"); break; + case Qt::AltModifier: + return QLatin1Literal("Alt"); + break; + case Qt::MetaModifier: + return QLatin1Literal("Meta"); + break; default: qFatal("Unexpected keyboard modifier"); return QLatin1String(); @@ -1280,6 +1306,7 @@ void tst_QSpinBox::wheelEvents_data() #if QT_CONFIG(wheelevent) QTest::addColumn("angleDelta"); QTest::addColumn("qt4Delta"); + QTest::addColumn("stepModifier"); QTest::addColumn("modifier"); QTest::addColumn("source"); QTest::addColumn("start"); @@ -1290,8 +1317,14 @@ void tst_QSpinBox::wheelEvents_data() const auto directions = {true, false}; const auto modifierList = {Qt::NoModifier, + Qt::ShiftModifier, Qt::ControlModifier, - Qt::ShiftModifier}; + Qt::AltModifier, + Qt::MetaModifier}; + + const auto validStepModifierList = {Qt::NoModifier, + Qt::ControlModifier, + Qt::ShiftModifier}; const auto sources = {Qt::MouseEventNotSynthesized, Qt::MouseEventSynthesizedBySystem, @@ -1313,62 +1346,71 @@ void tst_QSpinBox::wheelEvents_data() if (modifierName.isEmpty()) continue; - const int steps = (modifier & Qt::ControlModifier ? 10 : 1) - * (up ? 1 : -1); + for (auto stepModifier : validStepModifierList) { + + const auto stepModifierName = modifierToName(stepModifier); + if (stepModifierName.isEmpty()) + continue; - for (auto source : sources) { + const int steps = (modifier & stepModifier ? 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 }; - } + 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); + 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; + 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%s%sWith%sKeyboardModifier%s", + fraction ? "half" : "full", + up ? "Up" : "Down", + stepModifierName.latin1(), + modifierName.latin1(), + sourceName.latin1()) + << angleDelta + << units + << static_cast(stepModifier) + << modifiers + << source + << startValue + << expectedValues; } - - 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; } } } @@ -1383,6 +1425,7 @@ void tst_QSpinBox::wheelEvents() #if QT_CONFIG(wheelevent) QFETCH(QPoint, angleDelta); QFETCH(int, qt4Delta); + QFETCH(int, stepModifier); QFETCH(Qt::KeyboardModifiers, modifier); QFETCH(Qt::MouseEventSource, source); QFETCH(int, start); @@ -1392,6 +1435,11 @@ void tst_QSpinBox::wheelEvents() spinBox.setRange(-20, 20); spinBox.setValue(start); + QScopedPointer style( + new StepModifierStyle); + style->stepModifier = static_cast(stepModifier); + spinBox.setStyle(style.data()); + QWheelEvent event(QPointF(), QPointF(), QPoint(), angleDelta, qt4Delta, Qt::Vertical, Qt::NoButton, modifier, Qt::NoScrollPhase, source); @@ -1486,14 +1534,21 @@ void tst_QSpinBox::adaptiveDecimalStep() void tst_QSpinBox::stepModifierKeys_data() { QTest::addColumn("startValue"); + QTest::addColumn("stepModifier"); QTest::addColumn("keys"); QTest::addColumn("expectedValue"); const auto keyList = {Qt::Key_Up, Qt::Key_Down}; const auto modifierList = {Qt::NoModifier, + Qt::ShiftModifier, Qt::ControlModifier, - Qt::ShiftModifier}; + Qt::AltModifier, + Qt::MetaModifier}; + + const auto validStepModifierList = {Qt::NoModifier, + Qt::ControlModifier, + Qt::ShiftModifier}; for (auto key : keyList) { @@ -1511,17 +1566,26 @@ void tst_QSpinBox::stepModifierKeys_data() if (modifierName.isEmpty()) continue; - const int steps = (modifier & Qt::ControlModifier ? 10 : 1) - * (up ? 1 : -1); + for (auto stepModifier : validStepModifierList) { + + const auto stepModifierName = modifierToName(stepModifier); + if (stepModifierName.isEmpty()) + continue; + + const int steps = (modifier & stepModifier ? 10 : 1) + * (up ? 1 : -1); - const int expectedValue = startValue + steps; + const int expectedValue = startValue + steps; - QTest::addRow("%sWith%sKeyboardModifier", - up ? "up" : "down", - modifierName.latin1()) - << startValue - << keys - << expectedValue; + QTest::addRow("%s%sWith%sKeyboardModifier", + up ? "up" : "down", + stepModifierName.latin1(), + modifierName.latin1()) + << startValue + << static_cast(stepModifier) + << keys + << expectedValue; + } } } } @@ -1529,11 +1593,18 @@ void tst_QSpinBox::stepModifierKeys_data() void tst_QSpinBox::stepModifierKeys() { QFETCH(int, startValue); + QFETCH(int, stepModifier); QFETCH(QTestEventList, keys); QFETCH(int, expectedValue); QSpinBox spin(0); spin.setValue(startValue); + + QScopedPointer style( + new StepModifierStyle); + style->stepModifier = static_cast(stepModifier); + spin.setStyle(style.data()); + spin.show(); QVERIFY(QTest::qWaitForWindowActive(&spin)); @@ -1545,6 +1616,7 @@ void tst_QSpinBox::stepModifierKeys() void tst_QSpinBox::stepModifierButtons_data() { QTest::addColumn("subControl"); + QTest::addColumn("stepModifier"); QTest::addColumn("modifiers"); QTest::addColumn("startValue"); QTest::addColumn("expectedValue"); @@ -1552,8 +1624,14 @@ void tst_QSpinBox::stepModifierButtons_data() const auto subControls = {QStyle::SC_SpinBoxUp, QStyle::SC_SpinBoxDown}; const auto modifierList = {Qt::NoModifier, + Qt::ShiftModifier, Qt::ControlModifier, - Qt::ShiftModifier}; + Qt::AltModifier, + Qt::MetaModifier}; + + const auto validStepModifierList = {Qt::NoModifier, + Qt::ControlModifier, + Qt::ShiftModifier}; for (auto subControl : subControls) { @@ -1570,18 +1648,27 @@ void tst_QSpinBox::stepModifierButtons_data() if (modifierName.isEmpty()) continue; - const int steps = (modifier & Qt::ControlModifier ? 10 : 1) - * (up ? 1 : -1); + for (auto stepModifier : validStepModifierList) { + + const auto stepModifierName = modifierToName(stepModifier); + if (stepModifierName.isEmpty()) + continue; - const int expectedValue = startValue + steps; + const int steps = (modifier & stepModifier ? 10 : 1) + * (up ? 1 : -1); - QTest::addRow("%sWith%sKeyboardModifier", - up ? "up" : "down", - modifierName.latin1()) - << subControl - << modifiers - << startValue - << expectedValue; + const int expectedValue = startValue + steps; + + QTest::addRow("%s%sWith%sKeyboardModifier", + up ? "up" : "down", + stepModifierName.latin1(), + modifierName.latin1()) + << subControl + << static_cast(stepModifier) + << modifiers + << startValue + << expectedValue; + } } } } @@ -1589,6 +1676,7 @@ void tst_QSpinBox::stepModifierButtons_data() void tst_QSpinBox::stepModifierButtons() { QFETCH(QStyle::SubControl, subControl); + QFETCH(int, stepModifier); QFETCH(Qt::KeyboardModifiers, modifiers); QFETCH(int, startValue); QFETCH(int, expectedValue); @@ -1596,6 +1684,12 @@ void tst_QSpinBox::stepModifierButtons() SpinBox spin(0); spin.setRange(-20, 20); spin.setValue(startValue); + + QScopedPointer style( + new StepModifierStyle); + style->stepModifier = static_cast(stepModifier); + spin.setStyle(style.data()); + spin.show(); QVERIFY(QTest::qWaitForWindowActive(&spin)); @@ -1613,14 +1707,21 @@ void tst_QSpinBox::stepModifierButtons() void tst_QSpinBox::stepModifierPressAndHold_data() { QTest::addColumn("subControl"); + QTest::addColumn("stepModifier"); QTest::addColumn("modifiers"); QTest::addColumn("expectedStepModifier"); const auto subControls = {QStyle::SC_SpinBoxUp, QStyle::SC_SpinBoxDown}; const auto modifierList = {Qt::NoModifier, + Qt::ShiftModifier, Qt::ControlModifier, - Qt::ShiftModifier}; + Qt::AltModifier, + Qt::MetaModifier}; + + const auto validStepModifierList = {Qt::NoModifier, + Qt::ControlModifier, + Qt::ShiftModifier}; for (auto subControl : subControls) { @@ -1635,15 +1736,24 @@ void tst_QSpinBox::stepModifierPressAndHold_data() if (modifierName.isEmpty()) continue; - const int steps = (modifier & Qt::ControlModifier ? 10 : 1) - * (up ? 1 : -1); + for (auto stepModifier : validStepModifierList) { - QTest::addRow("%sWith%sKeyboardModifier", - up ? "up" : "down", - modifierName.latin1()) - << subControl - << modifiers - << steps; + const auto stepModifierName = modifierToName(stepModifier); + if (stepModifierName.isEmpty()) + continue; + + const int steps = (modifier & stepModifier ? 10 : 1) + * (up ? 1 : -1); + + QTest::addRow("%s%sWith%sKeyboardModifier", + up ? "up" : "down", + stepModifierName.latin1(), + modifierName.latin1()) + << subControl + << static_cast(stepModifier) + << modifiers + << steps; + } } } } @@ -1651,16 +1761,19 @@ void tst_QSpinBox::stepModifierPressAndHold_data() void tst_QSpinBox::stepModifierPressAndHold() { QFETCH(QStyle::SubControl, subControl); + QFETCH(int, stepModifier); QFETCH(Qt::KeyboardModifiers, modifiers); QFETCH(int, expectedStepModifier); SpinBox spin(0); - QScopedPointer pressAndHoldStyle( - new PressAndHoldStyle); - spin.setStyle(pressAndHoldStyle.data()); spin.setRange(-100, 100); spin.setValue(0); + QScopedPointer stepModifierStyle( + new StepModifierStyle(new PressAndHoldStyle)); + stepModifierStyle->stepModifier = static_cast(stepModifier); + spin.setStyle(stepModifierStyle.data()); + QSignalSpy spy(&spin, QOverload::of(&SpinBox::valueChanged)); spin.show(); -- cgit v1.2.3