Fix tracking of geometry restore with electric maximize
If the user wants to move a tiled window, but changes their mind and tiles the window back to the previous position, the geometryRestore() will be corrupted because initialMoveResizeGeometry() is the same as the geometry of the window in the tiled mode. This change fixes tracking of the geometry restore by precomputing the geometry restore when starting interactive move. That way, if the window is untiled and tiled again without release left pointer button, the geometry restore will be set to the correct value in setQuickTileMode(). This change also adjusts the test suite so such a subtle case won't be broken again without noticing it.
This commit is contained in:
parent
b1d0928c8e
commit
77f712d3a7
3 changed files with 79 additions and 44 deletions
|
@ -386,15 +386,16 @@ void QuickTilingTest::testQuickTilingKeyboardMove()
|
|||
|
||||
void QuickTilingTest::testQuickTilingPointerMove_data()
|
||||
{
|
||||
QTest::addColumn<QPoint>("targetPos");
|
||||
QTest::addColumn<QPoint>("pointerPos");
|
||||
QTest::addColumn<QSize>("tileSize");
|
||||
QTest::addColumn<QuickTileMode>("expectedMode");
|
||||
|
||||
QTest::newRow("topRight") << QPoint(2559, 24) << QuickTileMode(QuickTileFlag::Top | QuickTileFlag::Right);
|
||||
QTest::newRow("right") << QPoint(2559, 512) << QuickTileMode(QuickTileFlag::Right);
|
||||
QTest::newRow("bottomRight") << QPoint(2559, 1023) << QuickTileMode(QuickTileFlag::Bottom | QuickTileFlag::Right);
|
||||
QTest::newRow("bottomLeft") << QPoint(0, 1023) << QuickTileMode(QuickTileFlag::Bottom | QuickTileFlag::Left);
|
||||
QTest::newRow("Left") << QPoint(0, 512) << QuickTileMode(QuickTileFlag::Left);
|
||||
QTest::newRow("topLeft") << QPoint(0, 24) << QuickTileMode(QuickTileFlag::Top | QuickTileFlag::Left);
|
||||
QTest::newRow("topRight") << QPoint(2559, 24) << QSize(640, 512) << QuickTileMode(QuickTileFlag::Top | QuickTileFlag::Right);
|
||||
QTest::newRow("right") << QPoint(2559, 512) << QSize(640, 1024) << QuickTileMode(QuickTileFlag::Right);
|
||||
QTest::newRow("bottomRight") << QPoint(2559, 1023) << QSize(640, 512) << QuickTileMode(QuickTileFlag::Bottom | QuickTileFlag::Right);
|
||||
QTest::newRow("bottomLeft") << QPoint(0, 1023) << QSize(640, 512) << QuickTileMode(QuickTileFlag::Bottom | QuickTileFlag::Left);
|
||||
QTest::newRow("Left") << QPoint(0, 512) << QSize(640, 1024) << QuickTileMode(QuickTileFlag::Left);
|
||||
QTest::newRow("topLeft") << QPoint(0, 24) << QSize(640, 512) << QuickTileMode(QuickTileFlag::Top | QuickTileFlag::Left);
|
||||
}
|
||||
|
||||
void QuickTilingTest::testQuickTilingPointerMove()
|
||||
|
@ -402,24 +403,10 @@ void QuickTilingTest::testQuickTilingPointerMove()
|
|||
using namespace KWayland::Client;
|
||||
|
||||
QScopedPointer<KWayland::Client::Surface> surface(Test::createSurface());
|
||||
QVERIFY(!surface.isNull());
|
||||
|
||||
QScopedPointer<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.data(), Test::CreationSetup::CreateOnly));
|
||||
QVERIFY(!shellSurface.isNull());
|
||||
|
||||
// wait for the initial configure event
|
||||
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.data(), &Test::XdgToplevel::configureRequested);
|
||||
QVERIFY(toplevelConfigureRequestedSpy.isValid());
|
||||
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
|
||||
QVERIFY(surfaceConfigureRequestedSpy.isValid());
|
||||
surface->commit(KWayland::Client::Surface::CommitFlag::None);
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
|
||||
QScopedPointer<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.data()));
|
||||
|
||||
// let's render
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
auto c = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue);
|
||||
|
||||
QVERIFY(c);
|
||||
QCOMPARE(workspace()->activeClient(), c);
|
||||
QCOMPARE(c->frameGeometry(), QRect(0, 0, 100, 50));
|
||||
|
@ -427,29 +414,52 @@ void QuickTilingTest::testQuickTilingPointerMove()
|
|||
QCOMPARE(c->maximizeMode(), MaximizeRestore);
|
||||
|
||||
// we have to receive a configure event when the client becomes active
|
||||
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.data(), &Test::XdgToplevel::configureRequested);
|
||||
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
QTRY_COMPARE(surfaceConfigureRequestedSpy.count(), 2);
|
||||
QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
|
||||
|
||||
// verify that basic quick tile mode works as expected, i.e. the window is going to be
|
||||
// tiled if the user drags it to a screen edge or a corner
|
||||
QSignalSpy quickTileChangedSpy(c, &AbstractClient::quickTileModeChanged);
|
||||
QVERIFY(quickTileChangedSpy.isValid());
|
||||
|
||||
workspace()->performWindowOperation(c, Options::UnrestrictedMoveOp);
|
||||
QCOMPARE(c, workspace()->moveResizeClient());
|
||||
QCOMPARE(Cursors::self()->mouse()->pos(), QPoint(49, 24));
|
||||
|
||||
QFETCH(QPoint, targetPos);
|
||||
QFETCH(QPoint, pointerPos);
|
||||
QFETCH(QSize, tileSize);
|
||||
quint32 timestamp = 1;
|
||||
kwinApp()->platform()->pointerMotion(targetPos, timestamp++);
|
||||
kwinApp()->platform()->pointerButtonPressed(BTN_LEFT, timestamp++);
|
||||
kwinApp()->platform()->pointerMotion(pointerPos, timestamp++);
|
||||
kwinApp()->platform()->pointerButtonReleased(BTN_LEFT, timestamp++);
|
||||
QCOMPARE(Cursors::self()->mouse()->pos(), targetPos);
|
||||
QVERIFY(!workspace()->moveResizeClient());
|
||||
|
||||
QCOMPARE(quickTileChangedSpy.count(), 1);
|
||||
QTEST(c->quickTileMode(), "expectedMode");
|
||||
QCOMPARE(c->geometryRestore(), QRect(0, 0, 100, 50));
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
|
||||
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), tileSize);
|
||||
|
||||
// verify that geometry restore is correct after user untiles the window, but changes
|
||||
// their mind and tiles the window again while still holding left button
|
||||
workspace()->performWindowOperation(c, Options::UnrestrictedMoveOp);
|
||||
QCOMPARE(c, workspace()->moveResizeClient());
|
||||
|
||||
kwinApp()->platform()->pointerButtonPressed(BTN_LEFT, timestamp++); // untile the window
|
||||
kwinApp()->platform()->pointerMotion(QPoint(1280, 1024) / 2, timestamp++);
|
||||
QCOMPARE(quickTileChangedSpy.count(), 2);
|
||||
QCOMPARE(c->quickTileMode(), QuickTileMode(QuickTileFlag::None));
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
QCOMPARE(surfaceConfigureRequestedSpy.count(), 3);
|
||||
QCOMPARE(false, toplevelConfigureRequestedSpy.last().first().toSize().isEmpty());
|
||||
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(100, 50));
|
||||
|
||||
kwinApp()->platform()->pointerMotion(pointerPos, timestamp++); // tile the window again
|
||||
kwinApp()->platform()->pointerButtonReleased(BTN_LEFT, timestamp++);
|
||||
QCOMPARE(quickTileChangedSpy.count(), 3);
|
||||
QTEST(c->quickTileMode(), "expectedMode");
|
||||
QCOMPARE(c->geometryRestore(), QRect(0, 0, 100, 50));
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
QCOMPARE(surfaceConfigureRequestedSpy.count(), 4);
|
||||
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), tileSize);
|
||||
}
|
||||
|
||||
void QuickTilingTest::testQuickTilingTouchMove_data()
|
||||
|
|
|
@ -991,6 +991,7 @@ bool AbstractClient::startInteractiveMoveResize()
|
|||
}
|
||||
|
||||
updateInitialMoveResizeGeometry();
|
||||
updateElectricGeometryRestore();
|
||||
checkUnrestrictedInteractiveMoveResize();
|
||||
Q_EMIT clientStartUserMovedResized(this);
|
||||
if (ScreenEdges::self()->isDesktopSwitchingMovingClients())
|
||||
|
@ -3053,6 +3054,35 @@ QRect AbstractClient::electricBorderMaximizeGeometry(const QPoint &pos) const
|
|||
return ret;
|
||||
}
|
||||
|
||||
void AbstractClient::updateElectricGeometryRestore()
|
||||
{
|
||||
m_electricGeometryRestore = geometryRestore();
|
||||
if (quickTileMode() == QuickTileMode(QuickTileFlag::None)) {
|
||||
if (!(maximizeMode() & MaximizeHorizontal)) {
|
||||
m_electricGeometryRestore.setX(x());
|
||||
m_electricGeometryRestore.setWidth(width());
|
||||
}
|
||||
if (!(maximizeMode() & MaximizeVertical)) {
|
||||
m_electricGeometryRestore.setY(y());
|
||||
m_electricGeometryRestore.setHeight(height());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QRect AbstractClient::quickTileGeometryRestore() const
|
||||
{
|
||||
if (quickTileMode() != QuickTileMode(QuickTileFlag::None)) {
|
||||
// If the window is tiled, geometryRestore() already has a good value.
|
||||
return geometryRestore();
|
||||
}
|
||||
|
||||
if (isElectricBorderMaximizing()) {
|
||||
return m_electricGeometryRestore;
|
||||
} else {
|
||||
return moveResizeGeometry();
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractClient::setQuickTileMode(QuickTileMode mode, bool keyboard)
|
||||
{
|
||||
// Only allow quick tile on a regular window.
|
||||
|
@ -3069,15 +3099,7 @@ void AbstractClient::setQuickTileMode(QuickTileMode mode, bool keyboard)
|
|||
m_quickTileMode = int(QuickTileFlag::None);
|
||||
setMaximize(false, false);
|
||||
} else {
|
||||
// If the window is tiled, geometryRestore() already has a good value.
|
||||
QRect effectiveGeometryRestore = geometryRestore();
|
||||
if (m_quickTileMode == QuickTileMode(QuickTileFlag::None)) {
|
||||
if (isElectricBorderMaximizing()) {
|
||||
effectiveGeometryRestore = initialInteractiveMoveResizeGeometry();
|
||||
} else {
|
||||
effectiveGeometryRestore = moveResizeGeometry();
|
||||
}
|
||||
}
|
||||
QRect effectiveGeometryRestore = quickTileGeometryRestore();
|
||||
m_quickTileMode = int(QuickTileFlag::Maximize);
|
||||
setMaximize(true, true);
|
||||
setGeometryRestore(effectiveGeometryRestore);
|
||||
|
@ -3169,7 +3191,7 @@ void AbstractClient::setQuickTileMode(QuickTileMode mode, bool keyboard)
|
|||
} else if (quickTileMode() == QuickTileMode(QuickTileFlag::None)) {
|
||||
// Not coming out of an existing tile, not shifting monitors, we're setting a brand new tile.
|
||||
// Store geometry first, so we can go out of this tile later.
|
||||
setGeometryRestore(moveResizeGeometry());
|
||||
setGeometryRestore(quickTileGeometryRestore());
|
||||
}
|
||||
|
||||
if (mode != QuickTileMode(QuickTileFlag::None)) {
|
||||
|
@ -3186,9 +3208,9 @@ void AbstractClient::setQuickTileMode(QuickTileMode mode, bool keyboard)
|
|||
if (mode == QuickTileMode(QuickTileFlag::None)) {
|
||||
m_quickTileMode = int(QuickTileFlag::None);
|
||||
// Untiling, so just restore geometry, and we're done.
|
||||
if (!geometryRestore().isValid()) // invalid if we started maximized and wait for placement
|
||||
setGeometryRestore(moveResizeGeometry());
|
||||
moveResize(geometryRestore());
|
||||
if (geometryRestore().isValid()) { // invalid if we started maximized and wait for placement
|
||||
moveResize(geometryRestore());
|
||||
}
|
||||
checkWorkspacePosition(); // Just in case it's a different screen
|
||||
}
|
||||
doSetQuickTileMode();
|
||||
|
|
|
@ -1030,6 +1030,8 @@ protected:
|
|||
return m_electricMaximizing;
|
||||
}
|
||||
QRect electricBorderMaximizeGeometry(const QPoint &pos) const;
|
||||
void updateElectricGeometryRestore();
|
||||
QRect quickTileGeometryRestore() const;
|
||||
void updateQuickTileMode(QuickTileMode newMode) {
|
||||
m_quickTileMode = newMode;
|
||||
}
|
||||
|
@ -1259,6 +1261,7 @@ private:
|
|||
|
||||
// electric border/quick tiling
|
||||
QuickTileMode m_electricMode = QuickTileFlag::None;
|
||||
QRect m_electricGeometryRestore;
|
||||
bool m_electricMaximizing = false;
|
||||
// The quick tile mode of this window.
|
||||
int m_quickTileMode = int(QuickTileFlag::None);
|
||||
|
|
Loading…
Reference in a new issue