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
This commit is contained in:
parent
1193b0da77
commit
64ce6259a9
3 changed files with 248 additions and 16 deletions
|
@ -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"
|
||||
|
|
146
screenedge.cpp
146
screenedge.cpp
|
@ -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()) {
|
||||
|
|
22
screenedge.h
22
screenedge.h
|
@ -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 ×tamp);
|
||||
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;
|
||||
|
|
Loading…
Reference in a new issue