KWin
Loading...
Searching...
No Matches
gestures.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2017 Martin Gräßlin <mgraesslin@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9#include "gestures.h"
10
11#include <QDebug>
12#include <QRect>
13#include <cmath>
14#include <functional>
15
16namespace KWin
17{
18
19Gesture::Gesture(QObject *parent)
20 : QObject(parent)
21{
22}
23
24Gesture::~Gesture() = default;
25
27 : Gesture(parent)
28{
29}
30
32
33void SwipeGesture::setStartGeometry(const QRect &geometry)
34{
35 setMinimumX(geometry.x());
36 setMinimumY(geometry.y());
37 setMaximumX(geometry.x() + geometry.width());
38 setMaximumY(geometry.y() + geometry.height());
39
40 Q_ASSERT(m_maximumX >= m_minimumX);
41 Q_ASSERT(m_maximumY >= m_minimumY);
42}
43
44qreal SwipeGesture::deltaToProgress(const QPointF &delta) const
45{
46 if (!m_minimumDeltaRelevant || m_minimumDelta.isNull()) {
47 return 1.0;
48 }
49
50 switch (m_direction) {
53 return std::min(std::abs(delta.y()) / std::abs(m_minimumDelta.y()), 1.0);
56 return std::min(std::abs(delta.x()) / std::abs(m_minimumDelta.x()), 1.0);
57 default:
58 Q_UNREACHABLE();
59 }
60}
61
62bool SwipeGesture::minimumDeltaReached(const QPointF &delta) const
63{
64 return deltaToProgress(delta) >= 1.0;
65}
66
68 : Gesture(parent)
69{
70}
71
73
74qreal PinchGesture::scaleDeltaToProgress(const qreal &scaleDelta) const
75{
76 return std::clamp(std::abs(scaleDelta - 1) / minimumScaleDelta(), 0.0, 1.0);
77}
78
79bool PinchGesture::minimumScaleDeltaReached(const qreal &scaleDelta) const
80{
81 return scaleDeltaToProgress(scaleDelta) >= 1.0;
82}
83
85 : QObject(parent)
86{
87}
88
90
92{
93 Q_ASSERT(!m_swipeGestures.contains(gesture));
94 auto connection = connect(gesture, &QObject::destroyed, this, std::bind(&GestureRecognizer::unregisterSwipeGesture, this, gesture));
95 m_destroyConnections.insert(gesture, connection);
96 m_swipeGestures << gesture;
97}
98
100{
101 auto it = m_destroyConnections.find(gesture);
102 if (it != m_destroyConnections.end()) {
103 disconnect(it.value());
104 m_destroyConnections.erase(it);
105 }
106 m_swipeGestures.removeAll(gesture);
107 if (m_activeSwipeGestures.removeOne(gesture)) {
108 Q_EMIT gesture->cancelled();
109 }
110}
111
113{
114 Q_ASSERT(!m_pinchGestures.contains(gesture));
115 auto connection = connect(gesture, &QObject::destroyed, this, std::bind(&GestureRecognizer::unregisterPinchGesture, this, gesture));
116 m_destroyConnections.insert(gesture, connection);
117 m_pinchGestures << gesture;
118}
119
121{
122 auto it = m_destroyConnections.find(gesture);
123 if (it != m_destroyConnections.end()) {
124 disconnect(it.value());
125 m_destroyConnections.erase(it);
126 }
127 m_pinchGestures.removeAll(gesture);
128 if (m_activePinchGestures.removeOne(gesture)) {
129 Q_EMIT gesture->cancelled();
130 }
131}
132
133int GestureRecognizer::startSwipeGesture(uint fingerCount, const QPointF &startPos, StartPositionBehavior startPosBehavior)
134{
135 m_currentFingerCount = fingerCount;
136 if (!m_activeSwipeGestures.isEmpty() || !m_activePinchGestures.isEmpty()) {
137 return 0;
138 }
139 int count = 0;
140 for (SwipeGesture *gesture : std::as_const(m_swipeGestures)) {
141 if (gesture->minimumFingerCountIsRelevant()) {
142 if (gesture->minimumFingerCount() > fingerCount) {
143 continue;
144 }
145 }
146 if (gesture->maximumFingerCountIsRelevant()) {
147 if (gesture->maximumFingerCount() < fingerCount) {
148 continue;
149 }
150 }
151 if (startPosBehavior == StartPositionBehavior::Relevant) {
152 if (gesture->minimumXIsRelevant()) {
153 if (gesture->minimumX() > startPos.x()) {
154 continue;
155 }
156 }
157 if (gesture->maximumXIsRelevant()) {
158 if (gesture->maximumX() < startPos.x()) {
159 continue;
160 }
161 }
162 if (gesture->minimumYIsRelevant()) {
163 if (gesture->minimumY() > startPos.y()) {
164 continue;
165 }
166 }
167 if (gesture->maximumYIsRelevant()) {
168 if (gesture->maximumY() < startPos.y()) {
169 continue;
170 }
171 }
172 }
173
174 // Only add gestures who's direction aligns with current swipe axis
175 switch (gesture->direction()) {
178 if (m_currentSwipeAxis == Axis::Horizontal) {
179 continue;
180 }
181 break;
184 if (m_currentSwipeAxis == Axis::Vertical) {
185 continue;
186 }
187 break;
189 Q_UNREACHABLE();
190 }
191
192 m_activeSwipeGestures << gesture;
193 count++;
194 Q_EMIT gesture->started();
195 }
196 return count;
197}
198
200{
201 m_currentDelta += delta;
202
203 SwipeDirection direction; // Overall direction
204 Axis swipeAxis;
205
206 // Pick an axis for gestures so horizontal ones don't change to vertical ones without lifting fingers
207 if (m_currentSwipeAxis == Axis::None) {
208 if (std::abs(m_currentDelta.x()) >= std::abs(m_currentDelta.y())) {
209 swipeAxis = Axis::Horizontal;
210 direction = m_currentDelta.x() < 0 ? SwipeDirection::Left : SwipeDirection::Right;
211 } else {
212 swipeAxis = Axis::Vertical;
213 direction = m_currentDelta.y() < 0 ? SwipeDirection::Up : SwipeDirection::Down;
214 }
215 if (std::abs(m_currentDelta.x()) >= 5 || std::abs(m_currentDelta.y()) >= 5) {
216 // only lock in a direction if the delta is big enough
217 // to prevent accidentally choosing the wrong direction
218 m_currentSwipeAxis = swipeAxis;
219 }
220 } else {
221 swipeAxis = m_currentSwipeAxis;
222 }
223
224 // Find the current swipe direction
225 switch (swipeAxis) {
226 case Axis::Vertical:
227 direction = m_currentDelta.y() < 0 ? SwipeDirection::Up : SwipeDirection::Down;
228 break;
229 case Axis::Horizontal:
230 direction = m_currentDelta.x() < 0 ? SwipeDirection::Left : SwipeDirection::Right;
231 break;
232 default:
233 Q_UNREACHABLE();
234 }
235
236 // Eliminate wrong gestures (takes two iterations)
237 for (int i = 0; i < 2; i++) {
238
239 if (m_activeSwipeGestures.isEmpty()) {
240 startSwipeGesture(m_currentFingerCount);
241 }
242
243 for (auto it = m_activeSwipeGestures.begin(); it != m_activeSwipeGestures.end();) {
244 auto g = static_cast<SwipeGesture *>(*it);
245
246 if (g->direction() != direction) {
247 // If a gesture was started from a touchscreen border never cancel it
248 if (!g->minimumXIsRelevant() || !g->maximumXIsRelevant() || !g->minimumYIsRelevant() || !g->maximumYIsRelevant()) {
249 Q_EMIT g->cancelled();
250 it = m_activeSwipeGestures.erase(it);
251 continue;
252 }
253 }
254
255 it++;
256 }
257 }
258
259 // Send progress update
260 for (SwipeGesture *g : std::as_const(m_activeSwipeGestures)) {
261 Q_EMIT g->progress(g->deltaToProgress(m_currentDelta));
262 Q_EMIT g->deltaProgress(m_currentDelta);
263 }
264}
265
266void GestureRecognizer::cancelActiveGestures()
267{
268 for (auto g : std::as_const(m_activeSwipeGestures)) {
269 Q_EMIT g->cancelled();
270 }
271 for (auto g : std::as_const(m_activePinchGestures)) {
272 Q_EMIT g->cancelled();
273 }
274 m_activeSwipeGestures.clear();
275 m_activePinchGestures.clear();
276 m_currentScale = 0;
277 m_currentDelta = QPointF(0, 0);
278 m_currentSwipeAxis = Axis::None;
279}
280
282{
283 cancelActiveGestures();
284 m_currentFingerCount = 0;
285 m_currentDelta = QPointF(0, 0);
286 m_currentSwipeAxis = Axis::None;
287}
288
290{
291 const QPointF delta = m_currentDelta;
292 for (auto g : std::as_const(m_activeSwipeGestures)) {
293 if (static_cast<SwipeGesture *>(g)->minimumDeltaReached(delta)) {
294 Q_EMIT g->triggered();
295 } else {
296 Q_EMIT g->cancelled();
297 }
298 }
299 m_activeSwipeGestures.clear();
300 m_currentFingerCount = 0;
301 m_currentDelta = QPointF(0, 0);
302 m_currentSwipeAxis = Axis::None;
303}
304
306{
307 m_currentFingerCount = fingerCount;
308 int count = 0;
309 if (!m_activeSwipeGestures.isEmpty() || !m_activePinchGestures.isEmpty()) {
310 return 0;
311 }
312 for (PinchGesture *gesture : std::as_const(m_pinchGestures)) {
313 if (gesture->minimumFingerCountIsRelevant()) {
314 if (gesture->minimumFingerCount() > fingerCount) {
315 continue;
316 }
317 }
318 if (gesture->maximumFingerCountIsRelevant()) {
319 if (gesture->maximumFingerCount() < fingerCount) {
320 continue;
321 }
322 }
323
324 // direction doesn't matter yet
325 m_activePinchGestures << gesture;
326 count++;
327 Q_EMIT gesture->started();
328 }
329 return count;
330}
331
332void GestureRecognizer::updatePinchGesture(qreal scale, qreal angleDelta, const QPointF &posDelta)
333{
334 m_currentScale = scale;
335
336 // Determine the direction of the swipe
337 PinchDirection direction;
338 if (scale < 1) {
339 direction = PinchDirection::Contracting;
340 } else {
341 direction = PinchDirection::Expanding;
342 }
343
344 // Eliminate wrong gestures (takes two iterations)
345 for (int i = 0; i < 2; i++) {
346 if (m_activePinchGestures.isEmpty()) {
347 startPinchGesture(m_currentFingerCount);
348 }
349
350 for (auto it = m_activePinchGestures.begin(); it != m_activePinchGestures.end();) {
351 auto g = static_cast<PinchGesture *>(*it);
352
353 if (g->direction() != direction) {
354 Q_EMIT g->cancelled();
355 it = m_activePinchGestures.erase(it);
356 continue;
357 }
358 it++;
359 }
360 }
361
362 for (PinchGesture *g : std::as_const(m_activePinchGestures)) {
363 Q_EMIT g->progress(g->scaleDeltaToProgress(scale));
364 }
365}
366
368{
369 cancelActiveGestures();
370 m_currentScale = 1;
371 m_currentFingerCount = 0;
372 m_currentSwipeAxis = Axis::None;
373}
374
375void GestureRecognizer::endPinchGesture() // because fingers up
376{
377 for (auto g : std::as_const(m_activePinchGestures)) {
378 if (g->minimumScaleDeltaReached(m_currentScale)) {
379 Q_EMIT g->triggered();
380 } else {
381 Q_EMIT g->cancelled();
382 }
383 }
384 m_activeSwipeGestures.clear();
385 m_activePinchGestures.clear();
386 m_currentScale = 1;
387 m_currentFingerCount = 0;
388 m_currentSwipeAxis = Axis::None;
389}
390
392{
393 return m_maximumFingerCountRelevant;
394}
395
397{
398 return m_minimumFingerCount;
399}
400
402{
403 m_minimumFingerCount = count;
404 m_minimumFingerCountRelevant = true;
405}
406
408{
409 return m_minimumFingerCountRelevant;
410}
411
413{
414 m_maximumFingerCount = count;
415 m_maximumFingerCountRelevant = true;
416}
417
419{
420 return m_maximumFingerCount;
421}
422
424{
425 return m_direction;
426}
427
429{
430 m_direction = direction;
431}
432
434{
435 m_minimumX = x;
436 m_minimumXRelevant = true;
437}
438
440{
441 return m_minimumX;
442}
443
445{
446 return m_minimumXRelevant;
447}
448
450{
451 m_minimumY = y;
452 m_minimumYRelevant = true;
453}
454
456{
457 return m_minimumY;
458}
459
461{
462 return m_minimumYRelevant;
463}
464
466{
467 m_maximumX = x;
468 m_maximumXRelevant = true;
469}
470
472{
473 return m_maximumX;
474}
475
477{
478 return m_maximumXRelevant;
479}
480
482{
483 m_maximumY = y;
484 m_maximumYRelevant = true;
485}
486
488{
489 return m_maximumY;
490}
491
493{
494 return m_maximumYRelevant;
495}
496
498{
499 return m_minimumDelta;
500}
501
502void SwipeGesture::setMinimumDelta(const QPointF &delta)
503{
504 m_minimumDelta = delta;
505 m_minimumDeltaRelevant = true;
506}
507
509{
510 return m_minimumDeltaRelevant;
511}
512
514{
515 return m_minimumFingerCountRelevant;
516}
517
519{
520 m_minimumFingerCount = count;
521 m_minimumFingerCountRelevant = true;
522}
523
525{
526 return m_minimumFingerCount;
527}
528
530{
531 return m_maximumFingerCountRelevant;
532}
533
535{
536 m_maximumFingerCount = count;
537 m_maximumFingerCountRelevant = true;
538}
539
541{
542 return m_maximumFingerCount;
543}
544
546{
547 return m_direction;
548}
549
551{
552 m_direction = direction;
553}
554
556{
557 return m_minimumScaleDelta;
558}
559
560void PinchGesture::setMinimumScaleDelta(const qreal &scaleDelta)
561{
562 m_minimumScaleDelta = scaleDelta;
563 m_minimumScaleDeltaRelevant = true;
564}
565
567{
568 return m_minimumScaleDeltaRelevant;
569}
570
572{
573 return startSwipeGesture(fingerCount, QPointF(), StartPositionBehavior::Irrelevant);
574}
575
576int GestureRecognizer::startSwipeGesture(const QPointF &startPos)
577{
578 return startSwipeGesture(1, startPos, StartPositionBehavior::Relevant);
579}
580
581}
582
583#include "moc_gestures.cpp"
Gesture(QObject *parent)
Definition gestures.cpp:19
~Gesture() override
void cancelled()
int startPinchGesture(uint fingerCount)
Definition gestures.cpp:305
void updateSwipeGesture(const QPointF &delta)
Definition gestures.cpp:199
void unregisterSwipeGesture(SwipeGesture *gesture)
Definition gestures.cpp:99
~GestureRecognizer() override
void registerSwipeGesture(SwipeGesture *gesture)
Definition gestures.cpp:91
GestureRecognizer(QObject *parent=nullptr)
Definition gestures.cpp:84
void updatePinchGesture(qreal scale, qreal angleDelta, const QPointF &posDelta)
Definition gestures.cpp:332
void registerPinchGesture(PinchGesture *gesture)
Definition gestures.cpp:112
int startSwipeGesture(uint fingerCount)
Definition gestures.cpp:571
void unregisterPinchGesture(PinchGesture *gesture)
Definition gestures.cpp:120
void setMinimumScaleDelta(const qreal &scaleDelta)
Definition gestures.cpp:560
void setDirection(PinchDirection direction)
Definition gestures.cpp:550
uint minimumFingerCount() const
Definition gestures.cpp:524
PinchGesture(QObject *parent=nullptr)
Definition gestures.cpp:67
bool minimumScaleDeltaReached(const qreal &scaleDelta) const
Definition gestures.cpp:79
bool minimumFingerCountIsRelevant() const
Definition gestures.cpp:513
bool isMinimumScaleDeltaRelevant() const
Definition gestures.cpp:566
~PinchGesture() override
PinchDirection direction() const
Definition gestures.cpp:545
void setMinimumFingerCount(uint count)
Definition gestures.cpp:518
bool maximumFingerCountIsRelevant() const
Definition gestures.cpp:529
qreal scaleDeltaToProgress(const qreal &scaleDelta) const
Definition gestures.cpp:74
void setMaximumFingerCount(uint count)
Definition gestures.cpp:534
uint maximumFingerCount() const
Definition gestures.cpp:540
qreal minimumScaleDelta() const
Definition gestures.cpp:555
void setDirection(SwipeDirection direction)
Definition gestures.cpp:428
void setMaximumY(int y)
Definition gestures.cpp:481
SwipeDirection direction() const
Definition gestures.cpp:423
qreal deltaToProgress(const QPointF &delta) const
Definition gestures.cpp:44
bool maximumFingerCountIsRelevant() const
Definition gestures.cpp:391
bool isMinimumDeltaRelevant() const
Definition gestures.cpp:508
void setMinimumX(int x)
Definition gestures.cpp:433
bool maximumYIsRelevant() const
Definition gestures.cpp:492
uint minimumFingerCount() const
Definition gestures.cpp:396
QPointF minimumDelta() const
Definition gestures.cpp:497
~SwipeGesture() override
bool maximumXIsRelevant() const
Definition gestures.cpp:476
uint maximumFingerCount() const
Definition gestures.cpp:418
SwipeGesture(QObject *parent=nullptr)
Definition gestures.cpp:26
void setMaximumX(int x)
Definition gestures.cpp:465
void setMinimumFingerCount(uint count)
Definition gestures.cpp:401
int maximumY() const
Definition gestures.cpp:487
void setMinimumY(int y)
Definition gestures.cpp:449
void setMinimumDelta(const QPointF &delta)
Definition gestures.cpp:502
bool minimumDeltaReached(const QPointF &delta) const
Definition gestures.cpp:62
int minimumX() const
Definition gestures.cpp:439
void setMaximumFingerCount(uint count)
Definition gestures.cpp:412
bool minimumYIsRelevant() const
Definition gestures.cpp:460
void setStartGeometry(const QRect &geometry)
Definition gestures.cpp:33
bool minimumFingerCountIsRelevant() const
Definition gestures.cpp:407
bool minimumXIsRelevant() const
Definition gestures.cpp:444
int minimumY() const
Definition gestures.cpp:455
int maximumX() const
Definition gestures.cpp:471
PinchDirection
Definition globals.h:123
KWIN_EXPORT xcb_connection_t * connection()
Definition xcb.h:19
SwipeDirection
Directions for swipe gestures.
Definition globals.h:115