Add support for resize only borders on Wayland

Summary:
This change adds support for resizing outside the window decoration
(e.g. setting borders to NoSide or None).

To support this a new Toplevel::inputGeometry() -> QRect method is
added which exposes the geometry adjusted by the margins provided by
the decoration. This is checked in InputRedirection when finding a
Toplevel at a given position. The logic for figuring out whether the
event should go to the decoration or the window already handled the
situation correctly, so no further changes are needed.

BUG: 364607
FIXED-IN: 5.8.1

Reviewers: #kwin, #plasma_on_wayland

Subscribers: plasma-devel, kwin

Tags: #plasma_on_wayland, #kwin

Differential Revision: https://phabricator.kde.org/D2787
icc-effect-5.14.5
Martin Gräßlin 2016-09-15 11:44:32 +02:00
parent fd6e4bb023
commit fb59b05488
6 changed files with 88 additions and 1 deletions

View File

@ -1627,4 +1627,12 @@ QRect AbstractClient::iconGeometry() const
return candidateGeom.translated(candidatePanel->pos());
}
QRect AbstractClient::inputGeometry() const
{
if (isDecorated()) {
return Toplevel::inputGeometry() + decoration()->resizeOnlyBorders();
}
return Toplevel::inputGeometry();
}
}

View File

@ -578,6 +578,8 @@ public:
*/
virtual void showContextHelp();
QRect inputGeometry() const override;
// TODO: remove boolean trap
static bool belongToSameApplication(const AbstractClient* c1, const AbstractClient* c2, bool active_hack = false);

View File

@ -70,6 +70,8 @@ private Q_SLOTS:
void testPressToMove();
void testTapToMove_data();
void testTapToMove();
void testResizeOutsideWindow_data();
void testResizeOutsideWindow();
private:
AbstractClient *showWindow(Test::ShellSurfaceType type);
@ -498,6 +500,69 @@ void DecorationInputTest::testTapToMove()
QCOMPARE(c->pos(), oldPos + offset2 + offset3);
}
void DecorationInputTest::testResizeOutsideWindow_data()
{
QTest::addColumn<Test::ShellSurfaceType>("type");
QTest::addColumn<Qt::Edge>("edge");
QTest::addColumn<Qt::CursorShape>("expectedCursor");
QTest::newRow("wlShell - left") << Test::ShellSurfaceType::WlShell << Qt::LeftEdge << Qt::SizeHorCursor;
QTest::newRow("xdgShellV5 - left") << Test::ShellSurfaceType::XdgShellV5 << Qt::LeftEdge << Qt::SizeHorCursor;
QTest::newRow("wlShell - right") << Test::ShellSurfaceType::WlShell << Qt::RightEdge << Qt::SizeHorCursor;
QTest::newRow("xdgShellV5 - right") << Test::ShellSurfaceType::XdgShellV5 << Qt::RightEdge << Qt::SizeHorCursor;
QTest::newRow("wlShell - bottom") << Test::ShellSurfaceType::WlShell << Qt::BottomEdge << Qt::SizeVerCursor;
QTest::newRow("xdgShellV5 - bottom") << Test::ShellSurfaceType::XdgShellV5 << Qt::BottomEdge << Qt::SizeVerCursor;
}
void DecorationInputTest::testResizeOutsideWindow()
{
// this test verifies that one can resize the window outside the decoration with NoSideBorder
// first adjust config
kwinApp()->config()->group("org.kde.kdecoration2").writeEntry("BorderSize", QStringLiteral("None"));
kwinApp()->config()->sync();
workspace()->slotReconfigure();
// now create window
QFETCH(Test::ShellSurfaceType, type);
AbstractClient *c = showWindow(type);
QVERIFY(c);
QVERIFY(c->isDecorated());
QVERIFY(!c->noBorder());
c->move(screens()->geometry(0).center() - QPoint(c->width()/2, c->height()/2));
QVERIFY(c->geometry() != c->inputGeometry());
QVERIFY(c->inputGeometry().contains(c->geometry()));
QSignalSpy startMoveResizedSpy(c, &AbstractClient::clientStartUserMovedResized);
QVERIFY(startMoveResizedSpy.isValid());
// go to border
quint32 timestamp = 1;
QFETCH(Qt::Edge, edge);
switch (edge) {
case Qt::LeftEdge:
MOTION(QPoint(c->geometry().x() -1, c->geometry().center().y()));
break;
case Qt::RightEdge:
MOTION(QPoint(c->geometry().x() + c->geometry().width() +1, c->geometry().center().y()));
break;
case Qt::BottomEdge:
MOTION(QPoint(c->geometry().center().x(), c->geometry().y() + c->geometry().height() + 1));
break;
default:
break;
}
QVERIFY(!c->geometry().contains(KWin::Cursor::pos()));
// pressing should trigger resize
PRESS;
QVERIFY(!c->isResize());
QVERIFY(startMoveResizedSpy.wait());
QVERIFY(c->isResize());
RELEASE;
QVERIFY(!c->isResize());
}
}
WAYLANDTEST_MAIN(KWin::DecorationInputTest)

View File

@ -1451,7 +1451,7 @@ Toplevel *InputRedirection::findToplevel(const QPoint &pos)
continue;
}
}
if (t->geometry().contains(pos) && acceptsInput(t, pos)) {
if (t->inputGeometry().contains(pos) && acceptsInput(t, pos)) {
return t;
}
} while (it != stacking.begin());

View File

@ -538,5 +538,10 @@ quint32 Toplevel::windowId() const
return window();
}
QRect Toplevel::inputGeometry() const
{
return geometry();
}
} // namespace

View File

@ -219,6 +219,13 @@ public:
**/
virtual quint32 windowId() const;
QRect geometry() const;
/**
* The geometry of the Toplevel which accepts input events. This might be larger
* than the actual geometry, e.g. to support resizing outside the window.
*
* Default implementation returns same as geometry.
**/
virtual QRect inputGeometry() const;
QSize size() const;
QPoint pos() const;
QRect rect() const;