Skip to content

Commit 825c40a

Browse files
authored
Merge pull request #4 from InputActions/swipe-angle-tolerance-property
make default swipe angle tolerance customizable
2 parents 3396ef7 + f42e242 commit 825c40a

7 files changed

Lines changed: 135 additions & 51 deletions

File tree

src/libinputactions/config/parsers/core.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,17 @@ void NodeParser<InputDeviceProperties>::parse(const Node *node, InputDevicePrope
452452
loadSetter(result, &InputDeviceProperties::setThumbPressure, pressureRangesNode->at("thumb"));
453453
loadSetter(result, &InputDeviceProperties::setPalmPressure, pressureRangesNode->at("palm"));
454454
}
455+
456+
if (const auto *swipeNode = node->mapAt("swipe")) {
457+
if (const auto *angleToleranceNode = swipeNode->at("angle_tolerance")) {
458+
const auto value = angleToleranceNode->as<qreal>();
459+
if (value < 0 || value > 45) {
460+
throw InvalidValueConfigException(angleToleranceNode, "Value must be between 0 and 45.");
461+
}
462+
463+
result.setSwipeAngleTolerance(value);
464+
}
465+
}
455466
}
456467

457468
template<>

src/libinputactions/handlers/MotionTriggerHandler.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ bool MotionTriggerHandler::handleMotion(const InputDevice *device, const PointDe
191191

192192
swipeEvent.setAngle(Math::atan2deg360(currentDelta));
193193
swipeEvent.setAverageAngle(Math::atan2deg360(totalDelta / m_swipeDeltas.size()));
194+
swipeEvent.setSender(device);
194195
swipeEvent.setDelta(Delta(delta.acceleratedHypot(), delta.unacceleratedHypot()));
195196
swipeEvent.setPointDelta({delta.accelerated() * m_swipeDeltaMultiplier, delta.unaccelerated() * m_swipeDeltaMultiplier});
196197
swipeEvent.setSpeed(speed);

src/libinputactions/input/devices/InputDeviceProperties.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <QStringList>
2222
#include <libinputactions/helpers/QVariant.h>
2323
#include <libinputactions/input/devices/InputDevice.h>
24+
#include <libinputactions/triggers/SwipeTrigger.h>
2425

2526
namespace InputActions
2627
{
@@ -55,6 +56,7 @@ void InputDeviceProperties::apply(const InputDeviceProperties &other)
5556
apply(m_touchpadLmrTapButtonMap, other.m_touchpadLmrTapButtonMap);
5657
apply(m_touchpadMotionThreshold2, other.m_touchpadMotionThreshold2);
5758
apply(m_touchpadMotionThreshold3, other.m_touchpadMotionThreshold3);
59+
apply(m_swipeAngleTolerance, other.m_swipeAngleTolerance);
5860
}
5961

6062
QString InputDeviceProperties::toString() const
@@ -172,4 +174,9 @@ qreal InputDeviceProperties::touchpadMotionThreshold3() const
172174
return m_touchpadMotionThreshold3.value_or(10);
173175
}
174176

177+
qreal InputDeviceProperties::swipeAngleTolerance() const
178+
{
179+
return m_swipeAngleTolerance.value_or(DEFAULT_SWIPE_ANGLE_TOLERANGE);
180+
}
181+
175182
}

src/libinputactions/input/devices/InputDeviceProperties.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ class InputDeviceProperties
5252
Q_PROPERTY(qreal touchpadMotionThreshold2 READ touchpadMotionThreshold2)
5353
Q_PROPERTY(qreal touchpadMotionThreshold3 READ touchpadMotionThreshold3)
5454

55+
Q_PROPERTY(qreal swipeAngleTolerance READ swipeAngleTolerance)
56+
5557
public:
5658
InputDeviceProperties() = default;
5759
InputDeviceProperties(const InputDevice *device);
@@ -169,6 +171,12 @@ class InputDeviceProperties
169171
qreal touchpadMotionThreshold3() const;
170172
void setTouchpadMotionThreshold3(qreal value) { m_touchpadMotionThreshold3 = value; }
171173

174+
/**
175+
* Angle tolerance for left, right, up and down predefined directions. The remaining space is used for diagonals.
176+
*/
177+
qreal swipeAngleTolerance() const;
178+
void setSwipeAngleTolerance(qreal value) { m_swipeAngleTolerance = value; }
179+
172180
QString toString() const;
173181

174182
private:
@@ -197,6 +205,8 @@ class InputDeviceProperties
197205
std::optional<qreal> m_touchpadMotionThreshold2;
198206
std::optional<qreal> m_touchpadMotionThreshold3;
199207

208+
std::optional<qreal> m_swipeAngleTolerance;
209+
200210
friend class TestDeviceRuleNodeParser;
201211
};
202212

src/libinputactions/triggers/SwipeTrigger.cpp

Lines changed: 71 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -24,33 +24,6 @@
2424
namespace InputActions
2525
{
2626

27-
/**
28-
* Angle tolerance for left, up, right and down directions. Remaining space is used for diagonals.
29-
*/
30-
static const qreal ANGLE_TOLERANCE = 20;
31-
// clang-format off
32-
static const std::unordered_map<SwipeTriggerDirection, std::tuple<qreal, qreal, bool>> ANGLES{
33-
// direction // min angle // max angle // bidirectional
34-
{SwipeTriggerDirection::Left, {180 - ANGLE_TOLERANCE, 180 + ANGLE_TOLERANCE, false}},
35-
{SwipeTriggerDirection::Right, {360 - ANGLE_TOLERANCE, ANGLE_TOLERANCE, false}},
36-
{SwipeTriggerDirection::Up, {90 - ANGLE_TOLERANCE, 90 + ANGLE_TOLERANCE, false}},
37-
{SwipeTriggerDirection::Down, {270 - ANGLE_TOLERANCE, 270 + ANGLE_TOLERANCE, false}},
38-
39-
{SwipeTriggerDirection::LeftUp, {90 + ANGLE_TOLERANCE, 180 - ANGLE_TOLERANCE, false}},
40-
{SwipeTriggerDirection::LeftDown, {180 + ANGLE_TOLERANCE, 270 - ANGLE_TOLERANCE, false}},
41-
{SwipeTriggerDirection::RightUp, {ANGLE_TOLERANCE, 90 - ANGLE_TOLERANCE, false}},
42-
{SwipeTriggerDirection::RightDown, {270 + ANGLE_TOLERANCE, 360 - ANGLE_TOLERANCE, false}},
43-
44-
{SwipeTriggerDirection::LeftRight, {360 - ANGLE_TOLERANCE, ANGLE_TOLERANCE, true}},
45-
{SwipeTriggerDirection::UpDown, {270 - ANGLE_TOLERANCE, 270 + ANGLE_TOLERANCE, true}},
46-
47-
{SwipeTriggerDirection::LeftUpRightDown, {270 + ANGLE_TOLERANCE, 360 - ANGLE_TOLERANCE, true}},
48-
{SwipeTriggerDirection::LeftDownRightUp, {ANGLE_TOLERANCE, 90 - ANGLE_TOLERANCE, true}},
49-
50-
{SwipeTriggerDirection::Any, {0, 360, false}},
51-
};
52-
// clang-format on
53-
5427
SwipeTrigger::SwipeTrigger(qreal minAngle, qreal maxAngle)
5528
: MotionTrigger(TriggerType::Swipe)
5629
, m_minAngle(minAngle)
@@ -60,11 +33,16 @@ SwipeTrigger::SwipeTrigger(qreal minAngle, qreal maxAngle)
6033

6134
SwipeTrigger::SwipeTrigger(SwipeTriggerDirection direction)
6235
: MotionTrigger(TriggerType::Swipe)
36+
, m_direction(direction)
6337
{
64-
const auto &angles = ANGLES.at(direction);
65-
m_minAngle = std::get<0>(angles);
66-
m_maxAngle = std::get<1>(angles);
67-
m_bidirectional = std::get<2>(angles);
38+
switch (direction) {
39+
case SwipeTriggerDirection::LeftRight:
40+
case SwipeTriggerDirection::UpDown:
41+
case SwipeTriggerDirection::LeftUpRightDown:
42+
case SwipeTriggerDirection::LeftDownRightUp:
43+
m_bidirectional = true;
44+
break;
45+
}
6846
}
6947

7048
bool SwipeTrigger::canUpdate(const TriggerUpdateEvent &event) const
@@ -75,7 +53,9 @@ bool SwipeTrigger::canUpdate(const TriggerUpdateEvent &event) const
7553

7654
const auto &castedEvent = dynamic_cast<const SwipeTriggerUpdateEvent &>(event);
7755
const auto angle = castedEvent.averageAngle(); // Use the average so that the trigger is not cancelled on jitter
78-
return matchesAngleRange(angle) || (m_bidirectional && matchesOppositeAngleRange(angle));
56+
57+
const auto range = angleRange(castedEvent.sender());
58+
return matchesAngleRange(angle, range.min, range.max) || (m_bidirectional && matchesOppositeAngleRange(angle, range.min, range.max));
7959
}
8060

8161
void SwipeTrigger::updateActions(const TriggerUpdateEvent &event)
@@ -85,7 +65,8 @@ void SwipeTrigger::updateActions(const TriggerUpdateEvent &event)
8565

8666
// Ensure delta is always positive for normal angle range, and negative for opposite. Normal range takes priority over the opposite one in case of
8767
// overlapping.
88-
if (!matchesAngleRange(angle) && matchesOppositeAngleRange(angle)) {
68+
const auto range = angleRange(newEvent.sender());
69+
if (!matchesAngleRange(angle, range.min, range.max) && matchesOppositeAngleRange(angle, range.min, range.max)) {
8970
auto delta = event.delta();
9071
delta = {delta.accelerated() * -1, delta.unaccelerated() * -1};
9172
newEvent.setDelta(delta);
@@ -94,22 +75,66 @@ void SwipeTrigger::updateActions(const TriggerUpdateEvent &event)
9475
MotionTrigger::updateActions(newEvent);
9576
}
9677

97-
bool SwipeTrigger::matchesAngleRange(qreal angle) const
78+
SwipeTrigger::AngleRange SwipeTrigger::angleRange(const InputDevice *device) const
9879
{
99-
if (m_minAngle <= m_maxAngle) {
100-
return angle >= m_minAngle && angle <= m_maxAngle;
80+
if (!m_direction) {
81+
return {m_minAngle, m_maxAngle};
82+
}
83+
84+
const auto tolerance = device ? device->properties().swipeAngleTolerance() : DEFAULT_SWIPE_ANGLE_TOLERANGE;
85+
switch (m_direction.value()) {
86+
case SwipeTriggerDirection::Left:
87+
return {180 - tolerance, 180 + tolerance};
88+
case SwipeTriggerDirection::Right:
89+
return {360 - tolerance, tolerance};
90+
case SwipeTriggerDirection::Up:
91+
return {90 - tolerance, 90 + tolerance};
92+
case SwipeTriggerDirection::Down:
93+
return {270 - tolerance, 270 + tolerance};
94+
95+
case SwipeTriggerDirection::LeftUp:
96+
return {90 + tolerance, 180 - tolerance};
97+
case SwipeTriggerDirection::LeftDown:
98+
return {180 + tolerance, 270 - tolerance};
99+
case SwipeTriggerDirection::RightUp:
100+
return {tolerance, 90 - tolerance};
101+
case SwipeTriggerDirection::RightDown:
102+
return {270 + tolerance, 360 - tolerance};
103+
104+
case SwipeTriggerDirection::LeftRight:
105+
return {360 - tolerance, tolerance};
106+
case SwipeTriggerDirection::UpDown:
107+
return {270 - tolerance, 270 + tolerance};
108+
109+
case SwipeTriggerDirection::LeftUpRightDown:
110+
return {270 + tolerance, 360 - tolerance};
111+
case SwipeTriggerDirection::LeftDownRightUp:
112+
return {tolerance, 90 - tolerance};
113+
114+
case SwipeTriggerDirection::Any:
115+
return {0, 360};
116+
117+
default:
118+
return {0, 0};
101119
}
102-
return angle >= m_minAngle || angle <= m_maxAngle;
103120
}
104121

105-
bool SwipeTrigger::matchesOppositeAngleRange(qreal angle) const
122+
bool SwipeTrigger::matchesAngleRange(qreal angle, qreal min, qreal max)
106123
{
107-
auto min = m_minAngle - 180;
124+
if (min <= max) {
125+
return angle >= min && angle <= max;
126+
}
127+
return angle >= min || angle <= max;
128+
}
129+
130+
bool SwipeTrigger::matchesOppositeAngleRange(qreal angle, qreal min, qreal max)
131+
{
132+
min -= 180;
108133
if (min < 0) {
109134
min += 360;
110135
}
111136

112-
auto max = m_maxAngle - 180;
137+
max -= 180;
113138
if (max < 0) {
114139
max += 360;
115140
}
@@ -120,4 +145,10 @@ bool SwipeTrigger::matchesOppositeAngleRange(qreal angle) const
120145
return angle >= min || angle <= max;
121146
}
122147

148+
SwipeTrigger::AngleRange::AngleRange(qreal min, qreal max)
149+
: min(min)
150+
, max(max)
151+
{
152+
}
153+
123154
}

src/libinputactions/triggers/SwipeTrigger.h

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@
2323
namespace InputActions
2424
{
2525

26+
/**
27+
* Angle tolerance for left, right, up and down predefined directions. The remaining space is used for diagonals.
28+
*/
29+
static const qreal DEFAULT_SWIPE_ANGLE_TOLERANGE = 20;
30+
31+
class InputDevice;
32+
2633
class SwipeTriggerUpdateEvent : public MotionTriggerUpdateEvent
2734
{
2835
public:
@@ -40,9 +47,16 @@ class SwipeTriggerUpdateEvent : public MotionTriggerUpdateEvent
4047
qreal averageAngle() const { return m_averageAngle; }
4148
void setAverageAngle(qreal value) { m_averageAngle = value; }
4249

50+
/**
51+
* May be nullptr.
52+
*/
53+
const InputDevice *sender() const { return m_sender; }
54+
void setSender(const InputDevice *value) { m_sender = value; }
55+
4356
private:
4457
qreal m_angle{};
4558
qreal m_averageAngle{};
59+
const InputDevice *m_sender;
4660
};
4761

4862
/**
@@ -89,7 +103,7 @@ class SwipeTrigger : public MotionTrigger
89103
* If minAngle > maxAngle, the range includes all values where x >= minAngle || x <= maxAngle.
90104
*/
91105
SwipeTrigger(qreal minAngle, qreal maxAngle);
92-
SwipeTrigger(SwipeTriggerDirection swipeDirection);
106+
SwipeTrigger(SwipeTriggerDirection direction);
93107

94108
qreal minAngle() const { return m_minAngle; }
95109
qreal maxAngle() const { return m_maxAngle; }
@@ -106,11 +120,25 @@ class SwipeTrigger : public MotionTrigger
106120
void updateActions(const TriggerUpdateEvent &event) override;
107121

108122
private:
109-
bool matchesAngleRange(qreal angle) const;
110-
bool matchesOppositeAngleRange(qreal angle) const;
123+
struct AngleRange
124+
{
125+
AngleRange(qreal min, qreal max);
126+
127+
qreal min{};
128+
qreal max{};
129+
};
130+
/**
131+
* @param device Used for the angle tolerance. May be nullptr, in which case the default one is used.
132+
*/
133+
AngleRange angleRange(const InputDevice *device) const;
134+
135+
static bool matchesAngleRange(qreal angle, qreal min, qreal max);
136+
static bool matchesOppositeAngleRange(qreal angle, qreal min, qreal max);
137+
138+
std::optional<SwipeTriggerDirection> m_direction;
111139

112-
qreal m_minAngle;
113-
qreal m_maxAngle;
140+
qreal m_minAngle{};
141+
qreal m_maxAngle{};
114142
bool m_bidirectional{};
115143

116144
friend class TestSwipeTrigger;

tests/libinputactions/triggers/TestSwipeTrigger.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,7 @@ void TestSwipeTrigger::matchesAngleRange()
138138
QFETCH(qreal, angle);
139139
QFETCH(bool, result);
140140

141-
SwipeTrigger trigger(a, b);
142-
143-
QCOMPARE(trigger.matchesAngleRange(angle), result);
141+
QCOMPARE(SwipeTrigger::matchesAngleRange(angle, a, b), result);
144142
}
145143

146144
void TestSwipeTrigger::matchesOppositeAngleRange_data()
@@ -175,9 +173,7 @@ void TestSwipeTrigger::matchesOppositeAngleRange()
175173
QFETCH(qreal, angle);
176174
QFETCH(bool, result);
177175

178-
SwipeTrigger trigger(a, b);
179-
180-
QCOMPARE(trigger.matchesOppositeAngleRange(angle), result);
176+
QCOMPARE(SwipeTrigger::matchesOppositeAngleRange(angle, a, b), result);
181177
}
182178

183179
}

0 commit comments

Comments
 (0)