diff --git a/autotests/integration/outputchanges_test.cpp b/autotests/integration/outputchanges_test.cpp index e33cbbbcff..7977f7b179 100644 --- a/autotests/integration/outputchanges_test.cpp +++ b/autotests/integration/outputchanges_test.cpp @@ -10,6 +10,7 @@ #include "core/outputbackend.h" #include "core/outputconfiguration.h" #include "pointer_input.h" +#include "tiles/tilemanager.h" #include "wayland_server.h" #include "window.h" #include "workspace.h" @@ -42,6 +43,8 @@ private Q_SLOTS: void testMaximizedWindowRestoredAfterEnablingOutput(); void testFullScreenWindowRestoredAfterEnablingOutput(); void testQuickTiledWindowRestoredAfterEnablingOutput(); + void testCustomTiledWindowRestoredAfterEnablingOutput_data(); + void testCustomTiledWindowRestoredAfterEnablingOutput(); void testWindowRestoredAfterChangingScale(); void testMaximizeStateRestoredAfterEnablingOutput_data(); void testMaximizeStateRestoredAfterEnablingOutput(); @@ -526,6 +529,119 @@ void OutputChangesTest::testQuickTiledWindowRestoredAfterEnablingOutput() QCOMPARE(window->geometryRestore(), QRectF(1280 + 50, 100, 100, 50)); } +void OutputChangesTest::testCustomTiledWindowRestoredAfterEnablingOutput_data() +{ + const auto outputs = kwinApp()->outputBackend()->outputs(); + const size_t tileCount = workspace()->tileManager(outputs[1])->rootTile()->childTiles().size(); + + QTest::addColumn("tileIndex"); + for (size_t i = 0; i < tileCount; i++) { + QTest::addRow("tile %lu", i) << i; + } +} + +void OutputChangesTest::testCustomTiledWindowRestoredAfterEnablingOutput() +{ + // This test verifies that a custom tiled window will be moved to + // its original output and tile when the output is re-enabled + + const auto outputs = kwinApp()->outputBackend()->outputs(); + + // start with only one output + { + OutputConfiguration config; + auto changeSet = config.changeSet(outputs[1]); + changeSet->enabled = false; + workspace()->applyOutputConfiguration(config); + } + + // Create a window. + std::unique_ptr surface(Test::createSurface()); + std::unique_ptr shellSurface(Test::createXdgToplevelSurface(surface.get())); + const auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue); + QVERIFY(window); + + // kwin will send a configure event with the actived state. + QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested); + QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested); + QVERIFY(surfaceConfigureRequestedSpy.wait()); + + const QRectF originalGeometry = window->moveResizeGeometry(); + + // Enable the right output + { + OutputConfiguration config; + auto changeSet = config.changeSet(outputs[1]); + changeSet->enabled = true; + workspace()->applyOutputConfiguration(config); + } + + QFETCH(size_t, tileIndex); + const QRectF customTileGeom = workspace()->tileManager(outputs[1])->rootTile()->childTiles()[tileIndex]->windowGeometry(); + + // Move the window to the right monitor and put it in the middle tile. + QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged); + window->move(customTileGeom.topLeft() + QPointF(50, 50)); + const auto geomBeforeTiling = window->moveResizeGeometry(); + window->setQuickTileMode(QuickTileFlag::Custom, true); + + QVERIFY(surfaceConfigureRequestedSpy.wait()); + QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value(), customTileGeom.size().toSize()); + shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value()); + Test::render(surface.get(), customTileGeom.size().toSize(), Qt::blue); + QVERIFY(frameGeometryChangedSpy.wait()); + + QCOMPARE(window->frameGeometry(), customTileGeom); + QCOMPARE(window->moveResizeGeometry(), customTileGeom); + QCOMPARE(window->output(), outputs[1]); + QCOMPARE(window->quickTileMode(), QuickTileFlag::Custom); + QCOMPARE(window->requestedQuickTileMode(), QuickTileFlag::Custom); + QCOMPARE(window->geometryRestore(), geomBeforeTiling); + + // Disable the right output. + { + OutputConfiguration config; + auto changeSet = config.changeSet(outputs[1]); + changeSet->enabled = false; + workspace()->applyOutputConfiguration(config); + } + + // The window will be moved to the left monitor, and the original geometry restored + QVERIFY(surfaceConfigureRequestedSpy.wait()); + QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value(), originalGeometry.size().toSize()); + shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value()); + Test::render(surface.get(), originalGeometry.size().toSize(), Qt::blue); + QVERIFY(frameGeometryChangedSpy.wait()); + + QCOMPARE(window->frameGeometry(), originalGeometry); + QCOMPARE(window->moveResizeGeometry(), originalGeometry); + QCOMPARE(window->output(), outputs[0]); + QCOMPARE(window->quickTileMode(), QuickTileFlag::None); + QCOMPARE(window->requestedQuickTileMode(), QuickTileFlag::None); + + // Enable the right monitor again + { + OutputConfiguration config; + auto changeSet = config.changeSet(outputs[1]); + changeSet->enabled = true; + workspace()->applyOutputConfiguration(config); + } + + // The window will be moved back to the right monitor, and put in the correct tile + QVERIFY(surfaceConfigureRequestedSpy.wait()); + QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value(), customTileGeom.size().toSize()); + shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value()); + Test::render(surface.get(), customTileGeom.size().toSize(), Qt::blue); + QVERIFY(frameGeometryChangedSpy.wait()); + + QCOMPARE(window->frameGeometry(), customTileGeom); + QCOMPARE(window->moveResizeGeometry(), customTileGeom); + QCOMPARE(window->output(), outputs[1]); + QCOMPARE(window->quickTileMode(), QuickTileFlag::Custom); + QCOMPARE(window->requestedQuickTileMode(), QuickTileFlag::Custom); + QCOMPARE(window->geometryRestore(), geomBeforeTiling); +} + void OutputChangesTest::testWindowRestoredAfterChangingScale() { // This test verifies that a window will be moved to its original position after changing the scale of an output diff --git a/src/placementtracker.cpp b/src/placementtracker.cpp index b0fec348f8..c3af717abe 100644 --- a/src/placementtracker.cpp +++ b/src/placementtracker.cpp @@ -119,10 +119,15 @@ void PlacementTracker::restore(const QString &key) // setting the desired quick tile mode // TODO fix this more properly window->setQuickTileMode(QuickTileFlag::None, true); - window->setQuickTileMode(newData.quickTile, true); + if (newData.quickTile != QuickTileFlag::Custom) { + window->setQuickTileMode(newData.quickTile, true); + } window->setMaximize(newData.maximize & MaximizeMode::MaximizeVertical, newData.maximize & MaximizeMode::MaximizeHorizontal); window->setFullScreen(newData.fullscreen); window->moveResize(newData.geometry); + if (newData.quickTile == QuickTileFlag::Custom) { + window->setQuickTileMode(QuickTileFlag::Custom, true); + } window->setGeometryRestore(newData.geometryRestore); window->setFullscreenGeometryRestore(newData.fullscreenGeometryRestore); m_lastRestoreData[window] = dataForWindow(window); diff --git a/src/window.cpp b/src/window.cpp index 090c0de223..eeb14f0923 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -3559,19 +3559,12 @@ void Window::setQuickTileMode(QuickTileMode mode, bool keyboard) } } else if (mode == QuickTileMode(QuickTileFlag::Custom)) { // Custom tileMode is the only one that gets immediately assigned without a roundtrip - Tile *tile = nullptr; - if (keyboard) { - tile = workspace()->tileManager(output())->bestTileForPosition(moveResizeGeometry().center()); - } else { - Output *output = workspace()->outputAt(Cursors::self()->mouse()->pos()); - tile = workspace()->tileManager(output)->bestTileForPosition(Cursors::self()->mouse()->pos()); - } m_requestedQuickTileMode = mode; - setTile(tile); + setTile(workspace()->tileManager(workspace()->outputAt(whichScreen))->bestTileForPosition(whichScreen)); // Don't go into setTileMode as custom tiles don't go trough configure events return; } else { - Tile *newTile = workspace()->tileManager(output())->quickTile(m_requestedQuickTileMode); + Tile *newTile = workspace()->tileManager(moveResizeOutput())->quickTile(m_requestedQuickTileMode); if (newTile) { moveResize(newTile->absoluteGeometry()); } else if (tile()) {