aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Gräßlin <[email protected]>2017-03-19 11:40:03 +0100
committerMartin Gräßlin <[email protected]>2017-03-27 17:44:02 +0200
commitaa6c8f81168e4f89c67b9e88065aee675e306d1a (patch)
tree54219e7bf8d90ad6514bbffc8174a8d2fbd1b00c
parent22c91df2ec6c7dbe4cbb8410cd0e00147246fa6e (diff)
Add support for activating screenedges through touch swipe gestures
Summary: Each Edge creates a SwipeGesture for touch activation. The swipe needs to be a single finger starting from the edge into the screen for at least 20 %. The SwipeGesture and GestureRecognizer is extended to support the use cases of the touch screen edge swipe. New features supported by the gesture system are: * minimum and maximum position * a minimum delta for the swipe * progress signal based on the minimum delta * starting a swipe with a start point The Edge has the progress signal connected to its approach signal, thus visual feedback is provided through the screen edge effect. The screen edge system supports touch only for the edges (corners are too difficult to activate on touch screens). At the moment the following features are supported: * screen edge show/raise of windows (e.g. auto hidden panels) * trigger the configured action * trigger the configured callback function (e.g. script) In future it might make sense to add a touch specific configuration action to support different actions for screen edges activated by mouse and touch. BUG: 370323 Test Plan: configured a screen edge and triggered through touch, added an auto-hiding panel and triggered through touch Reviewers: #kwin, #plasma_on_wayland Subscribers: plasma-devel Tags: #plasma_on_wayland Differential Revision: https://phabricator.kde.org/D5106
-rw-r--r--autotests/CMakeLists.txt1
-rw-r--r--autotests/test_gestures.cpp235
-rw-r--r--gestures.cpp70
-rw-r--r--gestures.h88
-rw-r--r--input.cpp41
-rw-r--r--plugins/platforms/x11/standalone/edge.cpp4
-rw-r--r--plugins/platforms/x11/standalone/edge.h4
-rw-r--r--screenedge.cpp67
-rw-r--r--screenedge.h19
9 files changed, 513 insertions, 16 deletions
diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt
index bffe6c3..5e70e73 100644
--- a/autotests/CMakeLists.txt
+++ b/autotests/CMakeLists.txt
@@ -281,6 +281,7 @@ set( testScreenEdges_SRCS
mock_screens.cpp
mock_workspace.cpp
../atoms.cpp
+ ../gestures.cpp
../screens.cpp
../screenedge.cpp
../virtualdesktops.cpp
diff --git a/autotests/test_gestures.cpp b/autotests/test_gestures.cpp
index da32e72..a2600d7 100644
--- a/autotests/test_gestures.cpp
+++ b/autotests/test_gestures.cpp
@@ -34,6 +34,18 @@ private Q_SLOTS:
void testSwipeMaxFinger();
void testDirection_data();
void testDirection();
+ void testMinimumX_data();
+ void testMinimumX();
+ void testMinimumY_data();
+ void testMinimumY();
+ void testMaximumX_data();
+ void testMaximumX();
+ void testMaximumY_data();
+ void testMaximumY();
+ void testStartGeometry();
+ void testSetMinimumDelta();
+ void testMinimumDeltaReached_data();
+ void testMinimumDeltaReached();
void testUnregisterSwipeCancels();
void testDeleteSwipeCancels();
void testSwipeCancel_data();
@@ -45,6 +57,8 @@ private Q_SLOTS:
void testSwipeMinFingerStart();
void testSwipeMaxFingerStart_data();
void testSwipeMaxFingerStart();
+ void testSwipeGeometryStart_data();
+ void testSwipeGeometryStart();
void testSwipeDiagonalCancels_data();
void testSwipeDiagonalCancels();
};
@@ -119,6 +133,178 @@ void GestureTest::testDirection()
QCOMPARE(gesture.direction(), SwipeGesture::Direction::Down);
}
+void GestureTest::testMinimumX_data()
+{
+ QTest::addColumn<int>("min");
+
+ QTest::newRow("0") << 0;
+ QTest::newRow("-1") << -1;
+ QTest::newRow("1") << 1;
+}
+
+void GestureTest::testMinimumX()
+{
+ SwipeGesture gesture;
+ QCOMPARE(gesture.minimumX(), 0);
+ QCOMPARE(gesture.minimumXIsRelevant(), false);
+ QFETCH(int, min);
+ gesture.setMinimumX(min);
+ QCOMPARE(gesture.minimumX(), min);
+ QCOMPARE(gesture.minimumXIsRelevant(), true);
+}
+
+void GestureTest::testMinimumY_data()
+{
+ QTest::addColumn<int>("min");
+
+ QTest::newRow("0") << 0;
+ QTest::newRow("-1") << -1;
+ QTest::newRow("1") << 1;
+}
+
+void GestureTest::testMinimumY()
+{
+ SwipeGesture gesture;
+ QCOMPARE(gesture.minimumY(), 0);
+ QCOMPARE(gesture.minimumYIsRelevant(), false);
+ QFETCH(int, min);
+ gesture.setMinimumY(min);
+ QCOMPARE(gesture.minimumY(), min);
+ QCOMPARE(gesture.minimumYIsRelevant(), true);
+}
+
+void GestureTest::testMaximumX_data()
+{
+ QTest::addColumn<int>("max");
+
+ QTest::newRow("0") << 0;
+ QTest::newRow("-1") << -1;
+ QTest::newRow("1") << 1;
+}
+
+void GestureTest::testMaximumX()
+{
+ SwipeGesture gesture;
+ QCOMPARE(gesture.maximumX(), 0);
+ QCOMPARE(gesture.maximumXIsRelevant(), false);
+ QFETCH(int, max);
+ gesture.setMaximumX(max);
+ QCOMPARE(gesture.maximumX(), max);
+ QCOMPARE(gesture.maximumXIsRelevant(), true);
+}
+
+void GestureTest::testMaximumY_data()
+{
+ QTest::addColumn<int>("max");
+
+ QTest::newRow("0") << 0;
+ QTest::newRow("-1") << -1;
+ QTest::newRow("1") << 1;
+}
+
+void GestureTest::testMaximumY()
+{
+ SwipeGesture gesture;
+ QCOMPARE(gesture.maximumY(), 0);
+ QCOMPARE(gesture.maximumYIsRelevant(), false);
+ QFETCH(int, max);
+ gesture.setMaximumY(max);
+ QCOMPARE(gesture.maximumY(), max);
+ QCOMPARE(gesture.maximumYIsRelevant(), true);
+}
+
+void GestureTest::testStartGeometry()
+{
+ SwipeGesture gesture;
+ gesture.setStartGeometry(QRect(1, 2, 20, 30));
+ QCOMPARE(gesture.minimumXIsRelevant(), true);
+ QCOMPARE(gesture.minimumYIsRelevant(), true);
+ QCOMPARE(gesture.maximumXIsRelevant(), true);
+ QCOMPARE(gesture.maximumYIsRelevant(), true);
+ QCOMPARE(gesture.minimumX(), 1);
+ QCOMPARE(gesture.minimumY(), 2);
+ QCOMPARE(gesture.maximumX(), 21);
+ QCOMPARE(gesture.maximumY(), 32);
+}
+
+void GestureTest::testSetMinimumDelta()
+{
+ SwipeGesture gesture;
+ QCOMPARE(gesture.isMinimumDeltaRelevant(), false);
+ QCOMPARE(gesture.minimumDelta(), QSizeF());
+ QCOMPARE(gesture.minimumDeltaReached(QSizeF()), true);
+ gesture.setMinimumDelta(QSizeF(2, 3));
+ QCOMPARE(gesture.isMinimumDeltaRelevant(), true);
+ QCOMPARE(gesture.minimumDelta(), QSizeF(2, 3));
+ QCOMPARE(gesture.minimumDeltaReached(QSizeF()), false);
+ QCOMPARE(gesture.minimumDeltaReached(QSizeF(2, 3)), true);
+}
+
+void GestureTest::testMinimumDeltaReached_data()
+{
+ QTest::addColumn<KWin::SwipeGesture::Direction>("direction");
+ QTest::addColumn<QSizeF>("minimumDelta");
+ QTest::addColumn<QSizeF>("delta");
+ QTest::addColumn<bool>("reached");
+ QTest::addColumn<qreal>("progress");
+
+ QTest::newRow("Up (more)") << KWin::SwipeGesture::Direction::Up << QSizeF(0, -30) << QSizeF(0, -40) << true << 1.0;
+ QTest::newRow("Up (exact)") << KWin::SwipeGesture::Direction::Up << QSizeF(0, -30) << QSizeF(0, -30) << true << 1.0;
+ QTest::newRow("Up (less)") << KWin::SwipeGesture::Direction::Up << QSizeF(0, -30) << QSizeF(0, -29) << false << 29.0/30.0;
+ QTest::newRow("Left (more)") << KWin::SwipeGesture::Direction::Left << QSizeF(-30, -30) << QSizeF(-40, 20) << true << 1.0;
+ QTest::newRow("Left (exact)") << KWin::SwipeGesture::Direction::Left << QSizeF(-30, -40) << QSizeF(-30, 0) << true << 1.0;
+ QTest::newRow("Left (less)") << KWin::SwipeGesture::Direction::Left << QSizeF(-30, -30) << QSizeF(-29, 0) << false << 29.0/30.0;
+ QTest::newRow("Right (more)") << KWin::SwipeGesture::Direction::Right << QSizeF(30, -30) << QSizeF(40, 20) << true << 1.0;
+ QTest::newRow("Right (exact)") << KWin::SwipeGesture::Direction::Right << QSizeF(30, -40) << QSizeF(30, 0) << true << 1.0;
+ QTest::newRow("Right (less)") << KWin::SwipeGesture::Direction::Right << QSizeF(30, -30) << QSizeF(29, 0) << false << 29.0/30.0;
+ QTest::newRow("Down (more)") << KWin::SwipeGesture::Direction::Down << QSizeF(0, 30) << QSizeF(0, 40) << true << 1.0;
+ QTest::newRow("Down (exact)") << KWin::SwipeGesture::Direction::Down << QSizeF(0, 30) << QSizeF(0, 30) << true << 1.0;
+ QTest::newRow("Down (less)") << KWin::SwipeGesture::Direction::Down << QSizeF(0, 30) << QSizeF(0, 29) << false << 29.0/30.0;
+}
+
+void GestureTest::testMinimumDeltaReached()
+{
+ SwipeGesture gesture;
+ QFETCH(SwipeGesture::Direction, direction);
+ gesture.setDirection(direction);
+ QFETCH(QSizeF, minimumDelta);
+ gesture.setMinimumDelta(minimumDelta);
+ QFETCH(QSizeF, delta);
+ QFETCH(bool, reached);
+ QCOMPARE(gesture.minimumDeltaReached(delta), reached);
+
+ GestureRecognizer recognizer;
+ recognizer.registerGesture(&gesture);
+
+ QSignalSpy startedSpy(&gesture, &SwipeGesture::started);
+ QVERIFY(startedSpy.isValid());
+ QSignalSpy triggeredSpy(&gesture, &SwipeGesture::triggered);
+ QVERIFY(triggeredSpy.isValid());
+ QSignalSpy cancelledSpy(&gesture, &SwipeGesture::cancelled);
+ QVERIFY(cancelledSpy.isValid());
+ QSignalSpy progressSpy(&gesture, &SwipeGesture::progress);
+ QVERIFY(progressSpy.isValid());
+
+ recognizer.startSwipeGesture(1);
+ QCOMPARE(startedSpy.count(), 1);
+ QCOMPARE(triggeredSpy.count(), 0);
+ QCOMPARE(cancelledSpy.count(), 0);
+ QCOMPARE(progressSpy.count(), 0);
+
+ recognizer.updateSwipeGesture(delta);
+ QCOMPARE(startedSpy.count(), 1);
+ QCOMPARE(triggeredSpy.count(), 0);
+ QCOMPARE(cancelledSpy.count(), 0);
+ QCOMPARE(progressSpy.count(), 1);
+ QTEST(progressSpy.first().first().value<qreal>(), "progress");
+
+ recognizer.endSwipeGesture();
+ QCOMPARE(startedSpy.count(), 1);
+ QCOMPARE(progressSpy.count(), 1);
+ QCOMPARE(triggeredSpy.isEmpty(), !reached);
+ QCOMPARE(cancelledSpy.isEmpty(), reached);
+}
+
void GestureTest::testUnregisterSwipeCancels()
{
GestureRecognizer recognizer;
@@ -219,12 +405,21 @@ void GestureTest::testSwipeUpdateCancel()
QSignalSpy leftTriggeredSpy(&leftGesture, &SwipeGesture::triggered);
QVERIFY(leftTriggeredSpy.isValid());
+ QSignalSpy upProgressSpy(&upGesture, &SwipeGesture::progress);
+ QVERIFY(upProgressSpy.isValid());
+ QSignalSpy downProgressSpy(&downGesture, &SwipeGesture::progress);
+ QVERIFY(downProgressSpy.isValid());
+ QSignalSpy leftProgressSpy(&leftGesture, &SwipeGesture::progress);
+ QVERIFY(leftProgressSpy.isValid());
+ QSignalSpy rightProgressSpy(&rightGesture, &SwipeGesture::progress);
+ QVERIFY(rightProgressSpy.isValid());
+
recognizer.registerGesture(&upGesture);
recognizer.registerGesture(&downGesture);
recognizer.registerGesture(&rightGesture);
recognizer.registerGesture(&leftGesture);
- recognizer.startSwipeGesture(4);
+ QCOMPARE(recognizer.startSwipeGesture(4), 4);
// first a down gesture
recognizer.updateSwipeGesture(QSizeF(1, 20));
@@ -251,6 +446,11 @@ void GestureTest::testSwipeUpdateCancel()
QCOMPARE(downTriggeredSpy.count(), 0);
QCOMPARE(leftTriggeredSpy.count(), 0);
QCOMPARE(rightTriggeredSpy.count(), 0);
+
+ QCOMPARE(upProgressSpy.count(), 0);
+ QCOMPARE(downProgressSpy.count(), 0);
+ QCOMPARE(leftProgressSpy.count(), 0);
+ QCOMPARE(rightProgressSpy.count(), 0);
}
void GestureTest::testSwipeUpdateTrigger_data()
@@ -343,6 +543,39 @@ void GestureTest::testSwipeMaxFingerStart()
QTEST(!startedSpy.isEmpty(), "started");
}
+void GestureTest::testSwipeGeometryStart_data()
+{
+ QTest::addColumn<QRect>("geometry");
+ QTest::addColumn<QPointF>("startPos");
+ QTest::addColumn<bool>("started");
+
+ QTest::newRow("top left") << QRect(0, 0, 10, 20) << QPointF(0, 0) << true;
+ QTest::newRow("top right") << QRect(0, 0, 10, 20) << QPointF(10, 0) << true;
+ QTest::newRow("bottom left") << QRect(0, 0, 10, 20) << QPointF(0, 20) << true;
+ QTest::newRow("bottom right") << QRect(0, 0, 10, 20) << QPointF(10, 20) << true;
+ QTest::newRow("x too small") << QRect(10, 20, 30, 40) << QPointF(9, 25) << false;
+ QTest::newRow("y too small") << QRect(10, 20, 30, 40) << QPointF(25, 19) << false;
+ QTest::newRow("x too large") << QRect(10, 20, 30, 40) << QPointF(41, 25) << false;
+ QTest::newRow("y too large") << QRect(10, 20, 30, 40) << QPointF(25, 61) << false;
+ QTest::newRow("inside") << QRect(10, 20, 30, 40) << QPointF(25, 25) << true;
+}
+
+void GestureTest::testSwipeGeometryStart()
+{
+ GestureRecognizer recognizer;
+ SwipeGesture gesture;
+ QFETCH(QRect, geometry);
+ gesture.setStartGeometry(geometry);
+
+ QSignalSpy startedSpy(&gesture, &SwipeGesture::started);
+ QVERIFY(startedSpy.isValid());
+
+ recognizer.registerGesture(&gesture);
+ QFETCH(QPointF, startPos);
+ recognizer.startSwipeGesture(startPos);
+ QTEST(!startedSpy.isEmpty(), "started");
+}
+
void GestureTest::testSwipeDiagonalCancels_data()
{
QTest::addColumn<KWin::SwipeGesture::Direction>("direction");
diff --git a/gestures.cpp b/gestures.cpp
index 08ebd8f..511732d 100644
--- a/gestures.cpp
+++ b/gestures.cpp
@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "gestures.h"
-#include <QSizeF>
+#include <QRect>
#include <functional>
namespace KWin
@@ -39,6 +39,36 @@ SwipeGesture::SwipeGesture(QObject *parent)
SwipeGesture::~SwipeGesture() = default;
+void SwipeGesture::setStartGeometry(const QRect &geometry)
+{
+ setMinimumX(geometry.x());
+ setMinimumY(geometry.y());
+ setMaximumX(geometry.x() + geometry.width());
+ setMaximumY(geometry.y() + geometry.height());
+}
+
+qreal SwipeGesture::minimumDeltaReachedProgress(const QSizeF &delta) const
+{
+ if (!m_minimumDeltaRelevant || m_minimumDelta.isNull()) {
+ return 1.0;
+ }
+ switch (m_direction) {
+ case Direction::Up:
+ case Direction::Down:
+ return std::min(std::abs(delta.height()) / std::abs(m_minimumDelta.height()), 1.0);
+ case Direction::Left:
+ case Direction::Right:
+ return std::min(std::abs(delta.width()) / std::abs(m_minimumDelta.width()), 1.0);
+ default:
+ Q_UNREACHABLE();
+ }
+}
+
+bool SwipeGesture::minimumDeltaReached(const QSizeF &delta) const
+{
+ return minimumDeltaReachedProgress(delta) >= 1.0;
+}
+
GestureRecognizer::GestureRecognizer(QObject *parent)
: QObject(parent)
{
@@ -67,8 +97,9 @@ void GestureRecognizer::unregisterGesture(KWin::Gesture* gesture)
}
}
-void GestureRecognizer::startSwipeGesture(uint fingerCount)
+int GestureRecognizer::startSwipeGesture(uint fingerCount, const QPointF &startPos, StartPositionBehavior startPosBehavior)
{
+ int count = 0;
// TODO: verify that no gesture is running
for (Gesture *gesture : qAsConst(m_gestures)) {
SwipeGesture *swipeGesture = qobject_cast<SwipeGesture*>(gesture);
@@ -85,10 +116,34 @@ void GestureRecognizer::startSwipeGesture(uint fingerCount)
continue;
}
}
+ if (startPosBehavior == StartPositionBehavior::Relevant) {
+ if (swipeGesture->minimumXIsRelevant()) {
+ if (swipeGesture->minimumX() > startPos.x()) {
+ continue;
+ }
+ }
+ if (swipeGesture->maximumXIsRelevant()) {
+ if (swipeGesture->maximumX() < startPos.x()) {
+ continue;
+ }
+ }
+ if (swipeGesture->minimumYIsRelevant()) {
+ if (swipeGesture->minimumY() > startPos.y()) {
+ continue;
+ }
+ }
+ if (swipeGesture->maximumYIsRelevant()) {
+ if (swipeGesture->maximumY() < startPos.y()) {
+ continue;
+ }
+ }
+ }
// direction doesn't matter yet
m_activeSwipeGestures << swipeGesture;
+ count++;
emit swipeGesture->started();
}
+ return count;
}
void GestureRecognizer::updateSwipeGesture(const QSizeF &delta)
@@ -108,9 +163,13 @@ void GestureRecognizer::updateSwipeGesture(const QSizeF &delta)
// vertical
direction = delta.height() < 0 ? SwipeGesture::Direction::Up : SwipeGesture::Direction::Down;
}
+ const QSizeF combinedDelta = std::accumulate(m_swipeUpdates.constBegin(), m_swipeUpdates.constEnd(), QSizeF(0, 0));
for (auto it = m_activeSwipeGestures.begin(); it != m_activeSwipeGestures.end();) {
auto g = qobject_cast<SwipeGesture*>(*it);
if (g->direction() == direction) {
+ if (g->isMinimumDeltaRelevant()) {
+ emit g->progress(g->minimumDeltaReachedProgress(combinedDelta));
+ }
it++;
} else {
emit g->cancelled();
@@ -135,8 +194,13 @@ void GestureRecognizer::cancelSwipeGesture()
void GestureRecognizer::endSwipeGesture()
{
+ const QSizeF delta = std::accumulate(m_swipeUpdates.constBegin(), m_swipeUpdates.constEnd(), QSizeF(0, 0));
for (auto g : qAsConst(m_activeSwipeGestures)) {
- emit g->triggered();
+ if (static_cast<SwipeGesture*>(g)->minimumDeltaReached(delta)) {
+ emit g->triggered();
+ } else {
+ emit g->cancelled();
+ }
}
m_activeSwipeGestures.clear();
m_swipeUpdates.clear();
diff --git a/gestures.h b/gestures.h
index 802b515..3abbc9d 100644
--- a/gestures.h
+++ b/gestures.h
@@ -21,6 +21,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define KWIN_GESTURES_H
#include <QObject>
+#include <QPointF>
+#include <QSizeF>
#include <QMap>
#include <QVector>
@@ -95,12 +97,86 @@ public:
m_direction = direction;
}
+ void setMinimumX(int x) {
+ m_minimumX = x;
+ m_minimumXRelevant = true;
+ }
+ int minimumX() const {
+ return m_minimumX;
+ }
+ bool minimumXIsRelevant() const {
+ return m_minimumXRelevant;
+ }
+ void setMinimumY(int y) {
+ m_minimumY = y;
+ m_minimumYRelevant = true;
+ }
+ int minimumY() const {
+ return m_minimumY;
+ }
+ bool minimumYIsRelevant() const {
+ return m_minimumYRelevant;
+ }
+
+ void setMaximumX(int x) {
+ m_maximumX = x;
+ m_maximumXRelevant = true;
+ }
+ int maximumX() const {
+ return m_maximumX;
+ }
+ bool maximumXIsRelevant() const {
+ return m_maximumXRelevant;
+ }
+ void setMaximumY(int y) {
+ m_maximumY = y;
+ m_maximumYRelevant = true;
+ }
+ int maximumY() const {
+ return m_maximumY;
+ }
+ bool maximumYIsRelevant() const {
+ return m_maximumYRelevant;
+ }
+ void setStartGeometry(const QRect &geometry);
+
+ QSizeF minimumDelta() const {
+ return m_minimumDelta;
+ }
+ void setMinimumDelta(const QSizeF &delta) {
+ m_minimumDelta = delta;
+ m_minimumDeltaRelevant = true;
+ }
+ bool isMinimumDeltaRelevant() const {
+ return m_minimumDeltaRelevant;
+ }
+
+ qreal minimumDeltaReachedProgress(const QSizeF &delta) const;
+ bool minimumDeltaReached(const QSizeF &delta) const;
+
+Q_SIGNALS:
+ /**
+ * The progress of the gesture if a @link{minimumDelta} is set.
+ * The progress is reported in [0.0,1.0]
+ **/
+ void progress(qreal);
+
private:
bool m_minimumFingerCountRelevant = false;
uint m_minimumFingerCount = 0;
bool m_maximumFingerCountRelevant = false;
uint m_maximumFingerCount = 0;
Direction m_direction = Direction::Down;
+ bool m_minimumXRelevant = false;
+ int m_minimumX = 0;
+ bool m_minimumYRelevant = false;
+ int m_minimumY = 0;
+ bool m_maximumXRelevant = false;
+ int m_maximumX = 0;
+ bool m_maximumYRelevant = false;
+ int m_maximumY = 0;
+ bool m_minimumDeltaRelevant = false;
+ QSizeF m_minimumDelta;
};
class GestureRecognizer : public QObject
@@ -113,13 +189,23 @@ public:
void registerGesture(Gesture *gesture);
void unregisterGesture(Gesture *gesture);
- void startSwipeGesture(uint fingerCount);
+ int startSwipeGesture(uint fingerCount) {
+ return startSwipeGesture(fingerCount, QPointF(), StartPositionBehavior::Irrelevant);
+ }
+ int startSwipeGesture(const QPointF &startPos) {
+ return startSwipeGesture(1, startPos, StartPositionBehavior::Relevant);
+ }
void updateSwipeGesture(const QSizeF &delta);
void cancelSwipeGesture();
void endSwipeGesture();
private:
void cancelActiveSwipeGestures();
+ enum class StartPositionBehavior {
+ Relevant,
+ Irrelevant
+ };
+ int startSwipeGesture(uint fingerCount, const QPointF &startPos, StartPositionBehavior startPosBehavior);
QVector<Gesture*> m_gestures;
QVector<Gesture*> m_activeSwipeGestures;
QMap<Gesture*, QMetaObject::Connection> m_destroyConnections;
diff --git a/input.cpp b/input.cpp
index 59b422e..49e08b3 100644
--- a/input.cpp
+++ b/input.cpp
@@ -25,6 +25,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "touch_input.h"
#include "client.h"
#include "effects.h"
+#include "gestures.h"
#include "globalshortcuts.h"
#include "logind.h"
#include "main.h"
@@ -1047,6 +1048,46 @@ public:
// always forward
return false;
}
+ bool touchDown(quint32 id, const QPointF &pos, quint32 time) override {
+ Q_UNUSED(time)
+ // TODO: better check whether a touch sequence is in progess
+ if (m_touchInProgress || waylandServer()->seat()->isTouchSequence()) {
+ // cancel existing touch
+ ScreenEdges::self()->gestureRecognizer()->cancelSwipeGesture();
+ m_touchInProgress = false;
+ m_id = 0;
+ return false;
+ }
+ if (ScreenEdges::self()->gestureRecognizer()->startSwipeGesture(pos) > 0) {
+ m_touchInProgress = true;
+ m_id = id;
+ m_lastPos = pos;
+ return true;
+ }
+ return false;
+ }
+ bool touchMotion(quint32 id, const QPointF &pos, quint32 time) override {
+ Q_UNUSED(time)
+ if (m_touchInProgress && m_id == id) {
+ ScreenEdges::self()->gestureRecognizer()->updateSwipeGesture(QSizeF(pos.x() - m_lastPos.x(), pos.y() - m_lastPos.y()));
+ m_lastPos = pos;
+ return true;
+ }
+ return false;
+ }
+ bool touchUp(quint32 id, quint32 time) override {
+ Q_UNUSED(time)
+ if (m_touchInProgress && m_id == id) {
+ ScreenEdges::self()->gestureRecognizer()->endSwipeGesture();
+ m_touchInProgress = false;
+ return true;
+ }
+ return false;
+ }
+private:
+ bool m_touchInProgress = false;
+ quint32 m_id = 0;
+ QPointF m_lastPos;
};
/**
diff --git a/plugins/platforms/x11/standalone/edge.cpp b/plugins/platforms/x11/standalone/edge.cpp
index bf03cc0..544e78d 100644
--- a/plugins/platforms/x11/standalone/edge.cpp
+++ b/plugins/platforms/x11/standalone/edge.cpp
@@ -44,14 +44,14 @@ WindowBasedEdge::~WindowBasedEdge()
{
}
-void WindowBasedEdge::activate()
+void WindowBasedEdge::doActivate()
{
createWindow();
createApproachWindow();
doUpdateBlocking();
}
-void WindowBasedEdge::deactivate()
+void WindowBasedEdge::doDeactivate()
{
m_window.reset();
m_approachWindow.reset();
diff --git a/plugins/platforms/x11/standalone/edge.h b/plugins/platforms/x11/standalone/edge.h
index 44dc3df..980c8b3 100644
--- a/plugins/platforms/x11/standalone/edge.h
+++ b/plugins/platforms/x11/standalone/edge.h
@@ -50,8 +50,8 @@ public:
protected:
virtual void doGeometryUpdate();
- virtual void activate();
- virtual void deactivate();
+ virtual void doActivate() override;
+ virtual void doDeactivate() override;
virtual void doStartApproaching();
virtual void doStopApproaching();
virtual void doUpdateBlocking();
diff --git a/screenedge.cpp b/screenedge.cpp
index f28d1f4..9eecbe6 100644
--- a/screenedge.cpp
+++ b/screenedge.cpp
@@ -31,6 +31,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// KWin
#include "atoms.h"
+#include "gestures.h"
#include <client.h>
#include "cursor.h"
#include "main.h"
@@ -72,7 +73,33 @@ Edge::Edge(ScreenEdges *parent)
, m_blocked(false)
, m_pushBackBlocked(false)
, m_client(nullptr)
+ , m_gesture(new SwipeGesture(this))
{
+ m_gesture->setMinimumFingerCount(1);
+ m_gesture->setMaximumFingerCount(1);
+ connect(m_gesture, &Gesture::triggered, this,
+ [this] {
+ stopApproaching();
+ if (m_client) {
+ m_client->showOnScreenEdge();
+ unreserve();
+ return;
+ }
+ handleAction();
+ handleByCallback();
+ }, Qt::QueuedConnection
+ );
+ connect(m_gesture, &SwipeGesture::started, this, &Edge::startApproaching);
+ connect(m_gesture, &SwipeGesture::cancelled, this, &Edge::stopApproaching);
+ connect(m_gesture, &SwipeGesture::progress, this,
+ [this] (qreal progress) {
+ int factor = progress * 256.0f;
+ if (m_lastApproachingFactor != factor) {
+ m_lastApproachingFactor = factor;
+ emit approaching(border(), m_lastApproachingFactor/256.0f, m_approachGeometry);
+ }
+ }
+ );
}
Edge::~Edge()
@@ -436,14 +463,32 @@ void Edge::doUpdateBlocking()
void Edge::doGeometryUpdate()
{
+ if (isScreenEdge()) {
+ m_gesture->setStartGeometry(m_geometry);
+ m_gesture->setMinimumDelta(screens()->size(screens()->number(m_geometry.center())) * 0.2);
+ }
}
void Edge::activate()
{
+ if (isScreenEdge() && !m_edges->isDesktopSwitching()) {
+ m_edges->gestureRecognizer()->registerGesture(m_gesture);
+ }
+ doActivate();
+}
+
+void Edge::doActivate()
+{
}
void Edge::deactivate()
{
+ m_edges->gestureRecognizer()->unregisterGesture(m_gesture);
+ doDeactivate();
+}
+
+void Edge::doDeactivate()
+{
}
void Edge::startApproaching()
@@ -531,6 +576,27 @@ quint32 Edge::approachWindow() const
return 0;
}
+void Edge::setBorder(ElectricBorder border)
+{
+ m_border = border;
+ switch (m_border) {
+ case ElectricTop:
+ m_gesture->setDirection(SwipeGesture::Direction::Down);
+ break;
+ case ElectricRight:
+ m_gesture->setDirection(SwipeGesture::Direction::Left);
+ break;
+ case ElectricBottom:
+ m_gesture->setDirection(SwipeGesture::Direction::Up);
+ break;
+ case ElectricLeft:
+ m_gesture->setDirection(SwipeGesture::Direction::Right);
+ break;
+ default:
+ break;
+ }
+}
+
/**********************************************************
* ScreenEdges
*********************************************************/
@@ -551,6 +617,7 @@ ScreenEdges::ScreenEdges(QObject *parent)
, m_actionBottom(ElectricActionNone)
, m_actionBottomLeft(ElectricActionNone)
, m_actionLeft(ElectricActionNone)
+ , m_gestureRecognizer(new GestureRecognizer(this))
{
QWidget w;
m_cornerOffset = (w.physicalDpiX() + w.physicalDpiY() + 5) / 6;
diff --git a/screenedge.h b/screenedge.h
index 0c76b06..a65183b 100644
--- a/screenedge.h
+++ b/screenedge.h
@@ -44,7 +44,9 @@ class QMouseEvent;
namespace KWin {
class AbstractClient;
+class GestureRecognizer;
class ScreenEdges;
+class SwipeGesture;
class KWIN_EXPORT Edge : public QObject
{
@@ -103,12 +105,14 @@ protected:
const ScreenEdges *edges() const;
bool isBlocked() const;
virtual void doGeometryUpdate();
- virtual void activate();
- virtual void deactivate();
+ virtual void doActivate();
+ virtual void doDeactivate();
virtual void doStartApproaching();
virtual void doStopApproaching();
virtual void doUpdateBlocking();
private:
+ void activate();
+ void deactivate();
bool canActivate(const QPoint &cursorPos, const QDateTime &triggerTime);
void handle(const QPoint &cursorPos);
bool handleAction();
@@ -130,6 +134,7 @@ private:
bool m_blocked;
bool m_pushBackBlocked;
AbstractClient *m_client;
+ SwipeGesture *m_gesture;
};
/**
@@ -304,6 +309,10 @@ public:
ElectricBorderAction actionBottomLeft() const;
ElectricBorderAction actionLeft() const;
+ GestureRecognizer *gestureRecognizer() const {
+ return m_gestureRecognizer;
+ }
+
public Q_SLOTS:
void reconfigure();
/**
@@ -358,6 +367,7 @@ private:
ElectricBorderAction m_actionBottomLeft;
ElectricBorderAction m_actionLeft;
int m_cornerOffset;
+ GestureRecognizer *m_gestureRecognizer;
KWIN_SINGLETON(ScreenEdges)
};
@@ -412,11 +422,6 @@ inline void Edge::setAction(ElectricBorderAction action)
m_action = action;
}
-inline void Edge::setBorder(ElectricBorder border)
-{
- m_border = border;
-}
-
inline ScreenEdges *Edge::edges()
{
return m_edges;