Introduce dedicated actions for touch screen swipe gestures

Summary:
The new touch gestures activated for the same actions as configured for
mouse pointer actions. This has disadvantages as the only configured
default screen edge action cannot be triggered (corner) on touch. On the
other hand setting a default touch gesture would be rather annoying with
pointer as a default.

So overall it makes sense to split the actions and have dedicated pointer
and dedicated touch actions.

This change introduces the first part of it and splits the handling in
general. We now have:

Activates for pointer and touch:
 * client (auto-hiding panels)

Activates for pointer only:
 * the configured action
 * virtual desktop switching
 * callbacks

Activates for touch only:
 * the new touch action

The touch actions are implemented similar to the pointer actions which
slight improvements in the code which will be backported to the pointer
actions.

Introducing callbacks will be the next step. I plan to do it a little bit
different by using QActions as that's what KWin internally uses for
everything except screen edges.

Test Plan: Manual testing and improved auto tests

Reviewers: #kwin, #plasma

Subscribers: plasma-devel, kwin

Tags: #kwin

Differential Revision: https://phabricator.kde.org/D5252
icc-effect-5.14.5
Martin Gräßlin 2017-03-30 07:08:38 +02:00
parent 1193b0da77
commit 64ce6259a9
3 changed files with 248 additions and 16 deletions

View File

@ -21,6 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "../atoms.h"
#include "../cursor.h"
#include "../input.h"
#include "../gestures.h"
#include "../main.h"
#include "../screenedge.h"
#include "../screens.h"
@ -128,6 +129,7 @@ private Q_SLOTS:
void testPushBack();
void testFullScreenBlocking();
void testClientEdge();
void testTouchEdge();
};
void TestScreenEdges::initTestCase()
@ -334,6 +336,8 @@ void TestScreenEdges::testCreatingInitialEdges()
QCOMPARE(edges.size(), 8);
for (auto e : edges) {
QVERIFY(e->isReserved());
QCOMPARE(e->activatesForPointer(), true);
QCOMPARE(e->activatesForTouchGesture(), false);
}
static_cast<MockScreens*>(screens())->setGeometries(QList<QRect>{QRect{0, 0, 1024, 768}});
@ -382,6 +386,8 @@ void TestScreenEdges::testCreatingInitialEdges()
for (int i = 0; i < 8; ++i) {
auto e = edges.at(i);
QVERIFY(!e->isReserved());
QCOMPARE(e->activatesForPointer(), false);
QCOMPARE(e->activatesForTouchGesture(), false);
QCOMPARE(e->approachGeometry(), expectedGeometries.at(i*2+1));
}
}
@ -415,6 +421,8 @@ void TestScreenEdges::testCallback()
QCOMPARE(edges.size(), 10);
for (auto e: edges) {
QVERIFY(e->isReserved());
QCOMPARE(e->activatesForPointer(), true);
QCOMPARE(e->activatesForTouchGesture(), false);
}
auto it = std::find_if(edges.constBegin(), edges.constEnd(), [](Edge *e) {
return e->isScreenEdge() && e->isLeft() && e->approachGeometry().bottom() < 768;
@ -523,6 +531,8 @@ void TestScreenEdges::testCallback()
s->unreserve(ElectricLeft, &callback);
for (auto e: s->findChildren<Edge*>(QString(), Qt::FindDirectChildrenOnly)) {
QVERIFY(!e->isReserved());
QCOMPARE(e->activatesForPointer(), false);
QCOMPARE(e->activatesForTouchGesture(), false);
}
}
@ -739,6 +749,8 @@ void TestScreenEdges::testClientEdge()
QPointer<Edge> edge = s->findChildren<Edge*>().last();
QCOMPARE(edge->isReserved(), true);
QCOMPARE(edge->activatesForPointer(), true);
QCOMPARE(edge->activatesForTouchGesture(), true);
//remove old reserves and resize to be in the middle of the screen
s->reserve(&client, KWin::ElectricNone);
@ -844,6 +856,90 @@ void TestScreenEdges::testClientEdge()
QCOMPARE(Cursor::pos(), QPoint(1, 50));
}
void TestScreenEdges::testTouchEdge()
{
qRegisterMetaType<KWin::ElectricBorder>("ElectricBorder");
using namespace KWin;
auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
auto group = config->group("TouchEdges");
group.writeEntry("Top", "krunner");
group.writeEntry("Left", "krunner");
group.writeEntry("Bottom", "krunner");
group.writeEntry("Right", "krunner");
config->sync();
auto s = ScreenEdges::self();
s->setConfig(config);
s->init();
// we don't have multiple desktops, so it's returning false
QCOMPARE(s->isDesktopSwitching(), false);
QCOMPARE(s->isDesktopSwitchingMovingClients(), false);
QCOMPARE(s->actionTopLeft(), ElectricBorderAction::ElectricActionNone);
QCOMPARE(s->actionTop(), ElectricBorderAction::ElectricActionNone);
QCOMPARE(s->actionTopRight(), ElectricBorderAction::ElectricActionNone);
QCOMPARE(s->actionRight(), ElectricBorderAction::ElectricActionNone);
QCOMPARE(s->actionBottomRight(), ElectricBorderAction::ElectricActionNone);
QCOMPARE(s->actionBottom(), ElectricBorderAction::ElectricActionNone);
QCOMPARE(s->actionBottomLeft(), ElectricBorderAction::ElectricActionNone);
QCOMPARE(s->actionLeft(), ElectricBorderAction::ElectricActionNone);
QList<Edge*> edges = s->findChildren<Edge*>(QString(), Qt::FindDirectChildrenOnly);
QCOMPARE(edges.size(), 8);
for (auto e : edges) {
QCOMPARE(e->isReserved(), e->isScreenEdge());
QCOMPARE(e->activatesForPointer(), false);
QCOMPARE(e->activatesForTouchGesture(), e->isScreenEdge());
}
// try to activate the edge through pointer, should not be possible
auto it = std::find_if(edges.constBegin(), edges.constEnd(), [](Edge *e) {
return e->isScreenEdge() && e->isLeft();
});
QVERIFY(it != edges.constEnd());
QSignalSpy approachingSpy(s, &ScreenEdges::approaching);
QVERIFY(approachingSpy.isValid());
xcb_enter_notify_event_t event;
auto setPos = [&event] (const QPoint &pos) {
Cursor::setPos(pos);
event.root_x = pos.x();
event.root_y = pos.y();
event.event_x = pos.x();
event.event_y = pos.y();
};
event.root = XCB_WINDOW_NONE;
event.child = XCB_WINDOW_NONE;
event.event = (*it)->window();
event.same_screen_focus = 1;
event.time = QDateTime::currentMSecsSinceEpoch();
setPos(QPoint(0, 50));
QCOMPARE(s->isEntered(&event), false);
QVERIFY(approachingSpy.isEmpty());
s->gestureRecognizer()->startSwipeGesture(QPoint(0, 50));
QCOMPARE(approachingSpy.count(), 1);
s->gestureRecognizer()->cancelSwipeGesture();
QCOMPARE(approachingSpy.count(), 2);
// let's reconfigure
group.writeEntry("Top", "none");
group.writeEntry("Left", "none");
group.writeEntry("Bottom", "none");
group.writeEntry("Right", "none");
config->sync();
s->reconfigure();
edges = s->findChildren<Edge*>(QString(), Qt::FindDirectChildrenOnly);
QCOMPARE(edges.size(), 8);
for (auto e : edges) {
QCOMPARE(e->isReserved(), false);
QCOMPARE(e->activatesForPointer(), false);
QCOMPARE(e->activatesForTouchGesture(), false);
}
}
Q_CONSTRUCTOR_FUNCTION(forceXcb)
QTEST_MAIN(TestScreenEdges)
#include "test_screen_edges.moc"

View File

@ -85,8 +85,7 @@ Edge::Edge(ScreenEdges *parent)
unreserve();
return;
}
handleAction();
handleByCallback();
handleTouchAction();
}, Qt::QueuedConnection
);
connect(m_gesture, &SwipeGesture::started, this, &Edge::startApproaching);
@ -100,6 +99,17 @@ Edge::Edge(ScreenEdges *parent)
}
}
);
connect(this, &Edge::activatesForTouchGestureChanged, this,
[this] {
if (isReserved()) {
if (activatesForTouchGesture()) {
m_edges->gestureRecognizer()->registerGesture(m_gesture);
} else {
m_edges->gestureRecognizer()->unregisterGesture(m_gesture);
}
}
}
);
}
Edge::~Edge()
@ -140,11 +150,45 @@ void Edge::unreserve(QObject *object)
}
}
bool Edge::activatesForPointer() const
{
if (m_client) {
return true;
}
if (m_edges->isDesktopSwitching()) {
return true;
}
if (!m_callBacks.isEmpty()) {
return true;
}
if (m_action != ElectricActionNone) {
return true;
}
return false;
}
bool Edge::activatesForTouchGesture() const
{
if (!isScreenEdge()) {
return false;
}
if (m_client) {
return true;
}
if (m_touchAction != ElectricActionNone) {
return true;
}
return false;
}
bool Edge::triggersFor(const QPoint &cursorPos) const
{
if (isBlocked()) {
return false;
}
if (!activatesForPointer()) {
return false;
}
if (!m_geometry.contains(cursorPos)) {
return false;
}
@ -243,7 +287,7 @@ void Edge::handle(const QPoint &cursorPos)
return;
}
if (handleAction() || handleByCallback()) {
if (handlePointerAction() || handleByCallback()) {
pushCursorBack(cursorPos);
return;
}
@ -253,9 +297,9 @@ void Edge::handle(const QPoint &cursorPos)
}
}
bool Edge::handleAction()
bool Edge::handleAction(ElectricBorderAction action)
{
switch (m_action) {
switch (action) {
case ElectricActionShowDesktop: {
Workspace::self()->setShowingDesktop(!Workspace::self()->showingDesktop());
return true;
@ -439,6 +483,11 @@ void Edge::setGeometry(const QRect &geometry)
}
m_approachGeometry = QRect(x, y, width, height);
doGeometryUpdate();
if (isScreenEdge()) {
m_gesture->setStartGeometry(m_geometry);
m_gesture->setMinimumDelta(screens()->size(screens()->number(m_geometry.center())) * 0.2);
}
}
void Edge::checkBlocking()
@ -463,15 +512,11 @@ 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()) {
if (activatesForTouchGesture()) {
m_edges->gestureRecognizer()->registerGesture(m_gesture);
}
doActivate();
@ -597,6 +642,23 @@ void Edge::setBorder(ElectricBorder border)
}
}
void Edge::setTouchAction(ElectricBorderAction action) {
const bool wasTouch = activatesForTouchGesture();
m_touchAction = action;
if (wasTouch != activatesForTouchGesture()) {
emit activatesForTouchGestureChanged();
}
}
void Edge::setClient(AbstractClient *client)
{
const bool wasTouch = activatesForTouchGesture();
m_client = client;
if (wasTouch != activatesForTouchGesture()) {
emit activatesForTouchGestureChanged();
}
}
/**********************************************************
* ScreenEdges
*********************************************************/
@ -693,6 +755,12 @@ void ScreenEdges::reconfigure()
electricBorderAction(borderConfig.readEntry("BottomLeft", "None")));
setActionForBorder(ElectricLeft, &m_actionLeft,
electricBorderAction(borderConfig.readEntry("Left", "None")));
borderConfig = m_config->group("TouchEdges");
setActionForTouchBorder(ElectricTop, electricBorderAction(borderConfig.readEntry("Top", "None")));
setActionForTouchBorder(ElectricRight, electricBorderAction(borderConfig.readEntry("Right", "None")));
setActionForTouchBorder(ElectricBottom, electricBorderAction(borderConfig.readEntry("Bottom", "None")));
setActionForTouchBorder(ElectricLeft, electricBorderAction(borderConfig.readEntry("Left", "None")));
}
void ScreenEdges::setActionForBorder(ElectricBorder border, ElectricBorderAction *oldValue, ElectricBorderAction newValue)
@ -725,6 +793,44 @@ void ScreenEdges::setActionForBorder(ElectricBorder border, ElectricBorderAction
}
}
void ScreenEdges::setActionForTouchBorder(ElectricBorder border, ElectricBorderAction newValue)
{
auto it = m_touchActions.find(border);
ElectricBorderAction oldValue = ElectricActionNone;
if (it != m_touchActions.constEnd()) {
oldValue = it.value();
}
if (oldValue == newValue) {
return;
}
if (oldValue == ElectricActionNone) {
// have to reserve
for (auto it = m_edges.begin(); it != m_edges.end(); ++it) {
if ((*it)->border() == border) {
(*it)->reserve();
}
}
}
if (newValue == ElectricActionNone) {
// have to unreserve
for (auto it = m_edges.begin(); it != m_edges.end(); ++it) {
if ((*it)->border() == border) {
(*it)->unreserve();
}
}
m_touchActions.erase(it);
} else {
m_touchActions.insert(border, newValue);
}
// update action on all Edges for given border
for (auto it = m_edges.begin(); it != m_edges.end(); ++it) {
if ((*it)->border() == border) {
(*it)->setTouchAction(newValue);
}
}
}
void ScreenEdges::updateLayout()
{
const QSize desktopMatrix = VirtualDesktopManager::self()->grid().size();
@ -963,6 +1069,11 @@ Edge *ScreenEdges::createEdge(ElectricBorder border, int x, int y, int width, in
edge->reserve();
edge->setAction(action);
}
const ElectricBorderAction touchAction = actionForTouchEdge(edge);
if (touchAction != KWin::ElectricActionNone) {
edge->reserve();
edge->setTouchAction(touchAction);
}
}
if (isDesktopSwitching()) {
if (edge->isCorner()) {
@ -1009,6 +1120,15 @@ ElectricBorderAction ScreenEdges::actionForEdge(Edge *edge) const
return ElectricActionNone;
}
ElectricBorderAction ScreenEdges::actionForTouchEdge(Edge *edge) const
{
auto it = m_touchActions.find(edge->border());
if (it != m_touchActions.end()) {
return it.value();
}
return ElectricActionNone;
}
void ScreenEdges::reserveDesktopSwitching(bool isToReserve, Qt::Orientations o)
{
if (!o)
@ -1204,6 +1324,9 @@ bool ScreenEdges::isEntered(QMouseEvent *event)
if (!edge->isReserved()) {
continue;
}
if (!edge->activatesForPointer()) {
continue;
}
if (edge->approachGeometry().contains(event->globalPos())) {
if (!edge->isApproaching()) {
edge->startApproaching();
@ -1245,6 +1368,9 @@ bool ScreenEdges::handleEnterNotifiy(xcb_window_t window, const QPoint &point, c
if (!edge->isReserved()) {
continue;
}
if (!edge->activatesForPointer()) {
continue;
}
if (edge->window() == window) {
if (edge->check(point, timestamp)) {
if ((*it)->client()) {

View File

@ -75,6 +75,10 @@ public:
void setClient(AbstractClient *client);
AbstractClient *client() const;
const QRect &geometry() const;
void setTouchAction(ElectricBorderAction action);
bool activatesForPointer() const;
bool activatesForTouchGesture() const;
/**
* The window id of the native window representing the edge.
@ -100,6 +104,7 @@ public Q_SLOTS:
void checkBlocking();
Q_SIGNALS:
void approaching(ElectricBorder border, qreal factor, const QRect &geometry);
void activatesForTouchGestureChanged();
protected:
ScreenEdges *edges();
const ScreenEdges *edges() const;
@ -115,13 +120,20 @@ private:
void deactivate();
bool canActivate(const QPoint &cursorPos, const QDateTime &triggerTime);
void handle(const QPoint &cursorPos);
bool handleAction();
bool handleAction(ElectricBorderAction action);
bool handlePointerAction() {
return handleAction(m_action);
}
bool handleTouchAction() {
return handleAction(m_touchAction);
}
bool handleByCallback();
void switchDesktop(const QPoint &cursorPos);
void pushCursorBack(const QPoint &cursorPos);
ScreenEdges *m_edges;
ElectricBorder m_border;
ElectricBorderAction m_action;
ElectricBorderAction m_touchAction = ElectricActionNone;
int m_reserved;
QRect m_geometry;
QRect m_approachGeometry;
@ -345,7 +357,9 @@ private:
void createVerticalEdge(ElectricBorder border, const QRect &screen, const QRect &fullArea);
Edge *createEdge(ElectricBorder border, int x, int y, int width, int height, bool createAction = true);
void setActionForBorder(ElectricBorder border, ElectricBorderAction *oldValue, ElectricBorderAction newValue);
void setActionForTouchBorder(ElectricBorder border, ElectricBorderAction newValue);
ElectricBorderAction actionForEdge(Edge *edge) const;
ElectricBorderAction actionForTouchEdge(Edge *edge) const;
bool handleEnterNotifiy(xcb_window_t window, const QPoint &point, const QDateTime &timestamp);
bool handleDndNotify(xcb_window_t window, const QPoint &point);
void createEdgeForClient(AbstractClient *client, ElectricBorder border);
@ -366,6 +380,7 @@ private:
ElectricBorderAction m_actionBottom;
ElectricBorderAction m_actionBottomLeft;
ElectricBorderAction m_actionLeft;
QMap<ElectricBorder, ElectricBorderAction> m_touchActions;
int m_cornerOffset;
GestureRecognizer *m_gestureRecognizer;
@ -457,11 +472,6 @@ inline bool Edge::isBlocked() const
return m_blocked;
}
inline void Edge::setClient(AbstractClient *client)
{
m_client = client;
}
inline AbstractClient *Edge::client() const
{
return m_client;