diff --git a/autotests/integration/move_resize_window_test.cpp b/autotests/integration/move_resize_window_test.cpp index bfb579d6e3..b3d34f41de 100644 --- a/autotests/integration/move_resize_window_test.cpp +++ b/autotests/integration/move_resize_window_test.cpp @@ -969,6 +969,12 @@ void MoveResizeWindowTest::testCancelInteractiveMoveResize() QVERIFY(shellSurface != nullptr); Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue); QVERIFY(window); + QSignalSpy frameGeomtryChangedSpy(window, &Window::frameGeometryChanged); + QSignalSpy quickTileChangedSpy(window, &Window::quickTileModeChanged); + QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested); + QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested); + + QVERIFY(surfaceConfigureRequestedSpy.wait()); // tile / maximize window QFETCH(QuickTileMode, quickTileMode); @@ -978,21 +984,20 @@ void MoveResizeWindowTest::testCancelInteractiveMoveResize() } else { window->setQuickTileMode(quickTileMode, true); } - QCOMPARE(window->quickTileMode(), quickTileMode); + QCOMPARE(window->requestedQuickTileMode(), quickTileMode); QCOMPARE(window->requestedMaximizeMode(), maximizeMode); - QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested); - QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested); QVERIFY(surfaceConfigureRequestedSpy.wait()); + shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value()); Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue); - + QVERIFY(frameGeomtryChangedSpy.wait()); + QCOMPARE(window->quickTileMode(), quickTileMode); const QRectF geometry = window->moveResizeGeometry(); const QRectF geometryRestore = window->geometryRestore(); // Start resizing the client. QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted); QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished); - QCOMPARE(workspace()->moveResizeWindow(), nullptr); QCOMPARE(window->isInteractiveMove(), false); QCOMPARE(window->isInteractiveResize(), false); @@ -1002,12 +1007,22 @@ void MoveResizeWindowTest::testCancelInteractiveMoveResize() QCOMPARE(window->isInteractiveMove(), false); QCOMPARE(window->isInteractiveResize(), true); - Test::pointerMotionRelative(QPoint(1, 1), 1); + Test::pointerMotionRelative(QPoint(-10, -10), 1); + + QVERIFY(surfaceConfigureRequestedSpy.wait()); + shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value()); + Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue); + QVERIFY(frameGeomtryChangedSpy.wait()); QCOMPARE(window->quickTileMode(), QuickTileMode()); QCOMPARE(window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore); // cancel moveresize, all state from before should be restored window->keyPressEvent(Qt::Key::Key_Escape); + + QVERIFY(surfaceConfigureRequestedSpy.wait()); + shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value()); + Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue); + QVERIFY(frameGeomtryChangedSpy.wait()); QCOMPARE(window->moveResizeGeometry(), geometry); QCOMPARE(window->quickTileMode(), quickTileMode); QCOMPARE(window->requestedMaximizeMode(), maximizeMode); diff --git a/autotests/integration/quick_tiling_test.cpp b/autotests/integration/quick_tiling_test.cpp index 26e0760073..330c6e052b 100644 --- a/autotests/integration/quick_tiling_test.cpp +++ b/autotests/integration/quick_tiling_test.cpp @@ -6,6 +6,7 @@ SPDX-License-Identifier: GPL-2.0-or-later */ +#include "effect/globals.h" #include "kwin_wayland_test.h" #include "core/output.h" @@ -14,6 +15,7 @@ #include "decorations/settings.h" #include "pointer_input.h" #include "scripting/scripting.h" +#include "tiles/tilemanager.h" #include "utils/common.h" #include "wayland_server.h" #include "window.h" @@ -172,12 +174,15 @@ void QuickTilingTest::testQuickTiling() QFETCH(QuickTileMode, mode); QFETCH(QRectF, expectedGeometry); + const QuickTileMode oldQuickTileMode = window->quickTileMode(); window->setQuickTileMode(mode, true); - QCOMPARE(quickTileChangedSpy.count(), 1); + // at this point the geometry did not yet change QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50)); - // but quick tile mode already changed - QCOMPARE(window->quickTileMode(), mode); + // but requested quick tile mode already changed, proper quickTileMode not yet + QCOMPARE(window->requestedQuickTileMode(), mode); + // Actual quickTileMOde didn't change yet + QCOMPARE(window->quickTileMode(), oldQuickTileMode); // but we got requested a new geometry QVERIFY(surfaceConfigureRequestedSpy.wait()); @@ -191,6 +196,8 @@ void QuickTilingTest::testQuickTiling() QVERIFY(frameGeometryChangedSpy.wait()); QCOMPARE(frameGeometryChangedSpy.count(), 1); QCOMPARE(window->frameGeometry(), expectedGeometry); + QCOMPARE(quickTileChangedSpy.count(), 1); + QCOMPARE(window->quickTileMode(), mode); // send window to other screen QList outputs = workspace()->outputs(); @@ -200,9 +207,19 @@ void QuickTilingTest::testQuickTiling() // quick tile should not be changed QCOMPARE(window->quickTileMode(), mode); QTEST(window->frameGeometry(), "secondScreen"); + Tile *tile = workspace()->tileManager(outputs[1])->quickTile(mode); + QCOMPARE(window->tile(), tile); // now try to toggle again window->setQuickTileMode(mode, true); + QTEST(window->requestedQuickTileMode(), "expectedModeAfterToggle"); + QVERIFY(surfaceConfigureRequestedSpy.wait()); + QCOMPARE(surfaceConfigureRequestedSpy.count(), 3); + + shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value()); + Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).toSize(), Qt::red); + QVERIFY(quickTileChangedSpy.wait()); + QCOMPARE(quickTileChangedSpy.count(), 2); QTEST(window->quickTileMode(), "expectedModeAfterToggle"); } @@ -243,13 +260,14 @@ void QuickTilingTest::testQuickMaximizing() QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged); QSignalSpy maximizeChangedSpy(window, &Window::maximizedChanged); + const QuickTileMode oldQuickTileMode = window->quickTileMode(); window->setQuickTileMode(QuickTileFlag::Maximize, true); - QCOMPARE(quickTileChangedSpy.count(), 1); // at this point the geometry did not yet change QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50)); - // but quick tile mode already changed - QCOMPARE(window->quickTileMode(), QuickTileFlag::Maximize); + // but requested quick tile mode already changed + QCOMPARE(window->requestedQuickTileMode(), QuickTileFlag::Maximize); + QCOMPARE(window->quickTileMode(), oldQuickTileMode); QCOMPARE(window->geometryRestore(), QRect(0, 0, 100, 50)); // but we got requested a new geometry @@ -263,19 +281,19 @@ void QuickTilingTest::testQuickMaximizing() QVERIFY(frameGeometryChangedSpy.wait()); QCOMPARE(frameGeometryChangedSpy.count(), 1); + QCOMPARE(quickTileChangedSpy.count(), 1); + QCOMPARE(window->quickTileMode(), QuickTileFlag::Maximize); QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024)); QCOMPARE(window->geometryRestore(), QRect(0, 0, 100, 50)); // window is now set to maximised QCOMPARE(maximizeChangedSpy.count(), 1); QCOMPARE(window->maximizeMode(), MaximizeFull); - QCOMPARE(window->tile(), nullptr); // go back to quick tile none QFETCH(QuickTileMode, mode); window->setQuickTileMode(mode, true); - QCOMPARE(window->quickTileMode(), QuickTileMode(QuickTileFlag::None)); - QCOMPARE(quickTileChangedSpy.count(), 2); + QCOMPARE(window->requestedQuickTileMode(), QuickTileMode(QuickTileFlag::None)); // geometry not yet changed QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024)); QCOMPARE(window->geometryRestore(), QRect(0, 0, 100, 50)); @@ -286,10 +304,12 @@ void QuickTilingTest::testQuickMaximizing() // render again shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value()); - Test::render(surface.get(), QSize(100, 50), Qt::yellow); + Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).toSize(), Qt::yellow); QVERIFY(frameGeometryChangedSpy.wait()); QCOMPARE(frameGeometryChangedSpy.count(), 2); + QCOMPARE(quickTileChangedSpy.count(), 2); + QCOMPARE(window->quickTileMode(), QuickTileMode(QuickTileFlag::None)); QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50)); QCOMPARE(window->geometryRestore(), QRect(0, 0, 100, 50)); QCOMPARE(maximizeChangedSpy.count(), 2); @@ -324,6 +344,12 @@ void QuickTilingTest::testQuickTilingKeyboardMove() QCOMPARE(window->quickTileMode(), QuickTileMode(QuickTileFlag::None)); QCOMPARE(window->maximizeMode(), MaximizeRestore); + // We have to receive a configure event when the window becomes active. + QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested); + QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested); + QVERIFY(surfaceConfigureRequestedSpy.wait()); + QCOMPARE(surfaceConfigureRequestedSpy.count(), 1); + QSignalSpy quickTileChangedSpy(window, &Window::quickTileModeChanged); workspace()->performWindowOperation(window, Options::UnrestrictedMoveOp); @@ -355,6 +381,11 @@ void QuickTilingTest::testQuickTilingKeyboardMove() QCOMPARE(Cursors::self()->mouse()->pos(), targetPos); QVERIFY(!workspace()->moveResizeWindow()); + QVERIFY(surfaceConfigureRequestedSpy.wait()); + QCOMPARE(surfaceConfigureRequestedSpy.count(), 2); + shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value()); + Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).toSize(), Qt::red); + QVERIFY(quickTileChangedSpy.wait()); QCOMPARE(quickTileChangedSpy.count(), 1); QTEST(window->quickTileMode(), "expectedMode"); } @@ -406,8 +437,7 @@ void QuickTilingTest::testQuickTilingPointerMove() Test::pointerButtonPressed(BTN_LEFT, timestamp++); Test::pointerMotion(pointerPos, timestamp++); Test::pointerButtonReleased(BTN_LEFT, timestamp++); - QCOMPARE(quickTileChangedSpy.count(), 1); - QTEST(window->quickTileMode(), "expectedMode"); + QTEST(window->requestedQuickTileMode(), "expectedMode"); QCOMPARE(window->geometryRestore(), QRect(0, 0, 100, 50)); QVERIFY(surfaceConfigureRequestedSpy.wait()); QCOMPARE(surfaceConfigureRequestedSpy.count(), 2); @@ -418,6 +448,8 @@ void QuickTilingTest::testQuickTilingPointerMove() Test::render(surface.get(), tileSize, Qt::red); QVERIFY(frameGeometryChangedSpy.wait()); QCOMPARE(window->frameGeometry().size(), tileSize); + QCOMPARE(quickTileChangedSpy.count(), 1); + QTEST(window->quickTileMode(), "expectedMode"); // 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 @@ -426,8 +458,7 @@ void QuickTilingTest::testQuickTilingPointerMove() Test::pointerButtonPressed(BTN_LEFT, timestamp++); // untile the window Test::pointerMotion(QPoint(1280, 1024) / 2, timestamp++); - QCOMPARE(quickTileChangedSpy.count(), 2); - QCOMPARE(window->quickTileMode(), QuickTileMode(QuickTileFlag::None)); + QCOMPARE(window->requestedQuickTileMode(), QuickTileMode(QuickTileFlag::None)); QVERIFY(surfaceConfigureRequestedSpy.wait()); QCOMPARE(surfaceConfigureRequestedSpy.count(), 3); QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(100, 50)); @@ -437,15 +468,24 @@ void QuickTilingTest::testQuickTilingPointerMove() Test::render(surface.get(), QSize(100, 50), Qt::red); QVERIFY(frameGeometryChangedSpy.wait()); QCOMPARE(window->frameGeometry().size(), QSize(100, 50)); + QCOMPARE(quickTileChangedSpy.count(), 2); + QCOMPARE(window->quickTileMode(), QuickTileMode(QuickTileFlag::None)); Test::pointerMotion(pointerPos, timestamp++); // tile the window again Test::pointerButtonReleased(BTN_LEFT, timestamp++); - QCOMPARE(quickTileChangedSpy.count(), 3); - QTEST(window->quickTileMode(), "expectedMode"); + QTEST(window->requestedQuickTileMode(), "expectedMode"); QCOMPARE(window->geometryRestore(), QRect(0, 0, 100, 50)); QVERIFY(surfaceConfigureRequestedSpy.wait()); QCOMPARE(surfaceConfigureRequestedSpy.count(), 4); QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), tileSize); + + // attach a new image + shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value()); + Test::render(surface.get(), QSize(100, 50), Qt::red); + QVERIFY(frameGeometryChangedSpy.wait()); + QCOMPARE(window->frameGeometry().size(), QSize(100, 50)); + QCOMPARE(quickTileChangedSpy.count(), 3); + QTEST(window->quickTileMode(), "expectedMode"); } void QuickTilingTest::testQuickTilingTouchMove_data() @@ -512,11 +552,17 @@ void QuickTilingTest::testQuickTilingTouchMove() // TODO: we should test both cases with fixed fake decoration for autotests. const bool hasBorders = Workspace::self()->decorationBridge()->settings()->borderSize() != KDecoration2::BorderSize::None; - QCOMPARE(quickTileChangedSpy.count(), 1); - QTEST(window->quickTileMode(), "expectedMode"); + QTEST(window->requestedQuickTileMode(), "expectedMode"); QVERIFY(surfaceConfigureRequestedSpy.wait()); QTRY_COMPARE(surfaceConfigureRequestedSpy.count(), hasBorders ? 4 : 3); QCOMPARE(false, toplevelConfigureRequestedSpy.last().first().toSize().isEmpty()); + + // attach a new image + shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value()); + Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).toSize(), Qt::red); + QVERIFY(quickTileChangedSpy.wait()); + QCOMPARE(quickTileChangedSpy.count(), 1); + QTEST(window->quickTileMode(), "expectedMode"); } void QuickTilingTest::testX11QuickTiling_data() @@ -577,7 +623,6 @@ void QuickTilingTest::testX11QuickTiling() QCOMPARE(window->quickTileMode(), mode); QTEST(window->frameGeometry(), "expectedGeometry"); QCOMPARE(window->geometryRestore(), origGeo); - QEXPECT_FAIL("maximize", "For maximize we get two changed signals", Continue); QCOMPARE(quickTileChangedSpy.count(), 1); // quick tile to same edge again should also act like send to screen @@ -663,7 +708,6 @@ void QuickTilingTest::testX11QuickTilingAfterVertMaximize() window->setQuickTileMode(mode, true); QCOMPARE(window->quickTileMode(), mode); QTEST(window->frameGeometry(), "expectedGeometry"); - QEXPECT_FAIL("", "We get two changed events", Continue); QCOMPARE(quickTileChangedSpy.count(), 1); // and destroy the window again @@ -730,6 +774,9 @@ void QuickTilingTest::testShortcut() const int numberOfQuickTileActions = shortcutList.count(); + QSignalSpy quickTileChangedSpy(window, &Window::quickTileModeChanged); + QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged); + for (QString shortcut : shortcutList) { // invoke global shortcut through dbus auto msg = QDBusMessage::createMethodCall( @@ -739,28 +786,24 @@ void QuickTilingTest::testShortcut() QStringLiteral("invokeShortcut")); msg.setArguments(QList{shortcut}); QDBusConnection::sessionBus().asyncCall(msg); + + QVERIFY(surfaceConfigureRequestedSpy.wait()); + shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value()); + Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).toSize(), Qt::red); + QVERIFY(quickTileChangedSpy.wait()); } - QSignalSpy quickTileChangedSpy(window, &Window::quickTileModeChanged); + QCOMPARE(surfaceConfigureRequestedSpy.count(), numberOfQuickTileActions + 1); + QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), expectedGeometry.size()); + QCOMPARE(frameGeometryChangedSpy.count(), numberOfQuickTileActions); + QTRY_COMPARE(quickTileChangedSpy.count(), numberOfQuickTileActions); - // at this point the geometry did not yet change - QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50)); - // but quick tile mode already changed + // geometry already changed + QCOMPARE(window->frameGeometry(), expectedGeometry); + // quick tile mode already changed QTEST(window->quickTileMode(), "expectedMode"); - // but we got requested a new geometry - QVERIFY(surfaceConfigureRequestedSpy.wait()); - QCOMPARE(surfaceConfigureRequestedSpy.count(), 2); - QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), expectedGeometry.size()); - - // attach a new image - QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged); - shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value()); - Test::render(surface.get(), expectedGeometry.size(), Qt::red); - - QVERIFY(frameGeometryChangedSpy.wait()); QEXPECT_FAIL("maximize", "Geometry changed called twice for maximize", Continue); - QCOMPARE(frameGeometryChangedSpy.count(), 1); QCOMPARE(window->frameGeometry(), expectedGeometry); } @@ -825,16 +868,14 @@ void QuickTilingTest::testScript() QSignalSpy runningChangedSpy(s, &AbstractScript::runningChanged); s->run(); - QVERIFY(quickTileChangedSpy.wait()); - QCOMPARE(quickTileChangedSpy.count(), 1); - + QVERIFY(runningChangedSpy.wait()); QCOMPARE(runningChangedSpy.count(), 1); QCOMPARE(runningChangedSpy.first().first().toBool(), true); // at this point the geometry did not yet change QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50)); - // but quick tile mode already changed - QCOMPARE(window->quickTileMode(), expectedMode); + // but requested quick tile mode already changed + QCOMPARE(window->requestedQuickTileMode(), expectedMode); // but we got requested a new geometry QVERIFY(surfaceConfigureRequestedSpy.wait()); @@ -846,6 +887,8 @@ void QuickTilingTest::testScript() Test::render(surface.get(), expectedGeometry.size(), Qt::red); QVERIFY(frameGeometryChangedSpy.wait()); + QCOMPARE(quickTileChangedSpy.count(), 1); + QCOMPARE(window->quickTileMode(), expectedMode); QEXPECT_FAIL("maximize", "Geometry changed called twice for maximize", Continue); QCOMPARE(frameGeometryChangedSpy.count(), 1); QCOMPARE(window->frameGeometry(), expectedGeometry); diff --git a/src/placementtracker.cpp b/src/placementtracker.cpp index 9cb76311ea..b901093894 100644 --- a/src/placementtracker.cpp +++ b/src/placementtracker.cpp @@ -93,7 +93,7 @@ void PlacementTracker::restore(const QString &key) // don't touch windows where the user intentionally changed their state bool restore = window->interactiveMoveResizeCount() == newData.interactiveMoveResizeCount && window->requestedMaximizeMode() == newData.maximize - && window->quickTileMode() == newData.quickTile + && window->requestedQuickTileMode() == newData.quickTile && window->isFullScreen() == newData.fullscreen; if (!restore) { // the logic above can have false negatives if PlacementTracker changed the window state @@ -101,7 +101,7 @@ void PlacementTracker::restore(const QString &key) if (const auto it = m_lastRestoreData.find(window); it != m_lastRestoreData.end()) { restore = window->interactiveMoveResizeCount() == it->interactiveMoveResizeCount && window->requestedMaximizeMode() == it->maximize - && window->quickTileMode() == it->quickTile + && window->requestedQuickTileMode() == it->quickTile && window->isFullScreen() == it->fullscreen && window->moveResizeOutput()->uuid() == it->outputUuid; } diff --git a/src/tiles/quicktile.cpp b/src/tiles/quicktile.cpp index ffde9cd1a5..7ebe0c32ba 100644 --- a/src/tiles/quicktile.cpp +++ b/src/tiles/quicktile.cpp @@ -86,12 +86,16 @@ Tile *QuickRootTile::tileForMode(QuickTileMode mode) { switch (mode) { case QuickTileMode(QuickTileFlag::Left): + case QuickTileMode(QuickTileFlag::Left | QuickTileFlag::Vertical): return m_leftVerticalTile.get(); case QuickTileMode(QuickTileFlag::Right): + case QuickTileMode(QuickTileFlag::Right | QuickTileFlag::Vertical): return m_rightVerticalTile.get(); case QuickTileMode(QuickTileFlag::Top): + case QuickTileMode(QuickTileFlag::Top | QuickTileFlag::Horizontal): return m_topHorizontalTile.get(); case QuickTileMode(QuickTileFlag::Bottom): + case QuickTileMode(QuickTileFlag::Bottom | QuickTileFlag::Horizontal): return m_bottomHorizontalTile.get(); case QuickTileMode(QuickTileFlag::Left | QuickTileFlag::Top): return m_topLeftTile.get(); diff --git a/src/tiles/tile.cpp b/src/tiles/tile.cpp index cfe72d539d..00136553f1 100644 --- a/src/tiles/tile.cpp +++ b/src/tiles/tile.cpp @@ -41,6 +41,10 @@ Tile::~Tile() if (m_parentTile) { m_parentTile->removeChild(this); } + + if (m_tiling->tearingDown()) { + return; + } for (auto *w : std::as_const(m_windows)) { Tile *tile = m_tiling->bestTileForPosition(w->moveResizeGeometry().center()); w->setTile(tile); @@ -284,7 +288,6 @@ void Tile::addWindow(Window *window) if (!m_windows.contains(window)) { window->moveResize(windowGeometry()); m_windows.append(window); - window->setTile(this); Q_EMIT windowAdded(window); Q_EMIT windowsChanged(); } @@ -294,7 +297,6 @@ void Tile::removeWindow(Window *window) { // We already ensure there is a single copy of window in m_windows if (m_windows.removeOne(window)) { - window->setTile(nullptr); Q_EMIT windowRemoved(window); Q_EMIT windowsChanged(); } diff --git a/src/tiles/tilemanager.cpp b/src/tiles/tilemanager.cpp index fff0a0febf..9b7618ecca 100644 --- a/src/tiles/tilemanager.cpp +++ b/src/tiles/tilemanager.cpp @@ -75,6 +75,12 @@ TileManager::TileManager(Output *parent) TileManager::~TileManager() { + m_tearingDown = true; +} + +bool TileManager::tearingDown() const +{ + return m_tearingDown; } Output *TileManager::output() const diff --git a/src/tiles/tilemanager.h b/src/tiles/tilemanager.h index 7cd48cdcc9..d3838ae7ea 100644 --- a/src/tiles/tilemanager.h +++ b/src/tiles/tilemanager.h @@ -44,6 +44,8 @@ public: explicit TileManager(Output *parent = nullptr); ~TileManager() override; + bool tearingDown() const; + Output *output() const; KWin::Tile *bestTileForPosition(const QPointF &pos); @@ -69,6 +71,7 @@ private: std::unique_ptr m_rootTile = nullptr; std::unique_ptr m_quickRootTile = nullptr; std::unique_ptr m_tileModel = nullptr; + bool m_tearingDown = false; friend class CustomTile; }; diff --git a/src/window.cpp b/src/window.cpp index ce2435333f..fc019b043f 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -3426,6 +3426,15 @@ QRectF Window::quickTileGeometry(QuickTileMode mode, const QPointF &pos) const return workspace()->clientArea(MaximizeArea, this, pos); } +void Window::updateQuickTileMode(QuickTileMode newMode) +{ + if (m_requestedQuickTileMode == newMode) { + return; + } + m_requestedQuickTileMode = newMode; + doSetQuickTileMode(); +} + void Window::updateElectricGeometryRestore() { m_electricGeometryRestore = geometryRestore(); @@ -3469,20 +3478,20 @@ void Window::setQuickTileMode(QuickTileMode mode, bool keyboard) GeometryUpdatesBlocker blocker(this); - setTile(nullptr); + const QuickTileMode oldMode = requestedQuickTileMode(); + QPointF whichScreen = keyboard ? moveResizeGeometry().center() : Cursors::self()->mouse()->pos(); if (mode == QuickTileMode(QuickTileFlag::Maximize)) { if (requestedMaximizeMode() == MaximizeFull) { - m_quickTileMode = int(QuickTileFlag::None); + m_requestedQuickTileMode = QuickTileFlag::None; setMaximize(false, false); } else { QRectF effectiveGeometryRestore = quickTileGeometryRestore(); - m_quickTileMode = int(QuickTileFlag::Maximize); + m_requestedQuickTileMode = QuickTileFlag::Maximize; setMaximize(true, true); setGeometryRestore(effectiveGeometryRestore); } doSetQuickTileMode(); - Q_EMIT quickTileModeChanged(); return; } @@ -3496,31 +3505,26 @@ void Window::setQuickTileMode(QuickTileMode mode, bool keyboard) // restore from maximized so that it is possible to tile maximized windows with one hit or by dragging if (requestedMaximizeMode() != MaximizeRestore) { - if (mode != QuickTileMode(QuickTileFlag::None)) { - m_quickTileMode = int(QuickTileFlag::None); // Temporary, so the maximize code doesn't get all confused - + m_requestedQuickTileMode = QuickTileFlag::None; setMaximize(false, false); - moveResize(quickTileGeometry(mode, keyboard ? moveResizeGeometry().center() : Cursors::self()->mouse()->pos())); // Store the mode change - m_quickTileMode = mode; + m_requestedQuickTileMode = mode; } else { - m_quickTileMode = mode; + m_requestedQuickTileMode = mode; setMaximize(false, false); } doSetQuickTileMode(); - Q_EMIT quickTileModeChanged(); return; } - QPointF whichScreen = keyboard ? moveResizeGeometry().center() : Cursors::self()->mouse()->pos(); if (mode != QuickTileMode(QuickTileFlag::None)) { // If trying to tile to the side that the window is already tiled to move the window to the next // screen near the tile if it exists and swap the tile side, otherwise toggle the mode (set QuickTileFlag::None) - if (quickTileMode() == mode) { + if (oldMode == mode) { Output *currentOutput = moveResizeOutput(); Output *nextOutput = currentOutput; Output *candidateOutput = currentOutput; @@ -3554,18 +3558,16 @@ void Window::setQuickTileMode(QuickTileMode mode, bool keyboard) mode = (~mode & QuickTileFlag::Vertical) | (mode & QuickTileFlag::Horizontal); } } - } else if (quickTileMode() == QuickTileMode(QuickTileFlag::None)) { + } else if (oldMode == 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(quickTileGeometryRestore()); } - - m_quickTileMode = mode; + m_requestedQuickTileMode = mode; } if (mode == QuickTileMode(QuickTileFlag::None)) { - setTile(nullptr); - m_quickTileMode = int(QuickTileFlag::None); + m_requestedQuickTileMode = QuickTileFlag::None; QRectF geometry = moveResizeGeometry(); if (geometryRestore().isValid()) { @@ -3578,7 +3580,13 @@ void Window::setQuickTileMode(QuickTileMode mode, bool keyboard) anchor.y() - geometry.height() * offset.y())); } moveResize(geometry); + // Custom tiles need to be untiled immediately + if (oldMode == QuickTileFlag::Custom) { + setTile(nullptr); + return; + } } 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()); @@ -3586,26 +3594,44 @@ void Window::setQuickTileMode(QuickTileMode mode, bool keyboard) Output *output = workspace()->outputAt(Cursors::self()->mouse()->pos()); tile = workspace()->tileManager(output)->bestTileForPosition(Cursors::self()->mouse()->pos()); } + m_requestedQuickTileMode = mode; setTile(tile); + // Don't go into setTileMode as custom tiles don't go trough configure events + return; } else { - // Use whichScreen to move to next screen when retiling to the same edge as the old behavior - Output *output = workspace()->outputAt(whichScreen); - Tile *tile = workspace()->tileManager(output)->quickTile(mode); - setTile(tile); + Tile *newTile = workspace()->tileManager(output())->quickTile(m_requestedQuickTileMode); + if (newTile) { + moveResize(newTile->absoluteGeometry()); + } else if (tile()) { + moveResize(quickTileGeometryRestore()); + } } - doSetQuickTileMode(); - Q_EMIT quickTileModeChanged(); +} + +QuickTileMode Window::quickTileMode() const +{ + if (m_tile) { + return m_tile->quickTileMode(); + } else { + return QuickTileFlag::None; + } +} + +QuickTileMode Window::requestedQuickTileMode() const +{ + return m_requestedQuickTileMode; } void Window::setTile(Tile *tile) { if (m_tile == tile) { return; - } else if (m_tile) { - m_tile->removeWindow(this); } + Tile *oldTile = m_tile; + QuickTileMode oldTileMode = quickTileMode(); + m_tile = tile; if (m_tile) { @@ -3613,7 +3639,15 @@ void Window::setTile(Tile *tile) m_tile->addWindow(this); } + if (oldTile) { + oldTile->removeWindow(this); + } + Q_EMIT tileChanged(tile); + + if (oldTileMode != quickTileMode()) { + Q_EMIT quickTileModeChanged(); + } } Tile *Window::tile() const @@ -3695,8 +3729,11 @@ void Window::sendToOutput(Output *newOutput) const QRectF oldScreenArea = workspace()->clientArea(MaximizeArea, this, moveResizeOutput()); const QRectF screenArea = workspace()->clientArea(MaximizeArea, this, newOutput); - if (m_quickTileMode == QuickTileMode(QuickTileFlag::Custom)) { + if (requestedQuickTileMode() == QuickTileMode(QuickTileFlag::Custom)) { setTile(nullptr); + } else { + Tile *newTile = workspace()->tileManager(newOutput)->quickTile(requestedQuickTileMode()); + setTile(newTile); } QRectF newGeom = moveToArea(oldGeom, oldScreenArea, screenArea); diff --git a/src/window.h b/src/window.h index 73ef5fe5bd..7937843b11 100644 --- a/src/window.h +++ b/src/window.h @@ -1092,10 +1092,8 @@ public: * @param keyboard Defines whether to take keyboard cursor into account. */ void setQuickTileMode(QuickTileMode mode, bool keyboard = false); - QuickTileMode quickTileMode() const - { - return QuickTileMode(m_quickTileMode); - } + QuickTileMode quickTileMode() const; + QuickTileMode requestedQuickTileMode() const; Layer layer() const; void updateLayer(); @@ -1544,10 +1542,7 @@ protected: void updateElectricGeometryRestore(); QRectF quickTileGeometryRestore() const; QRectF quickTileGeometry(QuickTileMode mode, const QPointF &pos) const; - void updateQuickTileMode(QuickTileMode newMode) - { - m_quickTileMode = newMode; - } + void updateQuickTileMode(QuickTileMode newMode); // geometry handling void checkOffscreenPosition(QRectF *geom, const QRectF &screenArea); @@ -1788,8 +1783,8 @@ protected: QuickTileMode m_electricMode = QuickTileFlag::None; QRectF m_electricGeometryRestore; bool m_electricMaximizing = false; - // The quick tile mode of this window. - int m_quickTileMode = int(QuickTileFlag::None); + // The requested quick tile mode of this window. + QuickTileMode m_requestedQuickTileMode = QuickTileFlag::None; QTimer *m_electricMaximizingDelay = nullptr; // geometry diff --git a/src/x11window.cpp b/src/x11window.cpp index ca748833c0..e9884d4e7f 100644 --- a/src/x11window.cpp +++ b/src/x11window.cpp @@ -30,6 +30,7 @@ #include "scene/windowitem.h" #include "screenedge.h" #include "shadow.h" +#include "tiles/tilemanager.h" #include "virtualdesktops.h" #include "wayland/surface.h" #include "wayland_server.h" @@ -2599,6 +2600,11 @@ bool X11Window::acceptsFocus() const return info->input(); } +void X11Window::doSetQuickTileMode() +{ + setTile(workspace()->tileManager(output())->quickTile(m_requestedQuickTileMode)); +} + void X11Window::setBlockingCompositing(bool block) { const bool blocks = rules()->checkBlockCompositing(block && options->windowsBlockCompositing()); @@ -4430,6 +4436,8 @@ void X11Window::maximize(MaximizeMode mode) return; } + const auto currentQuickTileMode = requestedQuickTileMode(); + QRectF clientArea; if (isElectricBorderMaximizing()) { clientArea = workspace()->clientArea(MaximizeArea, this, interactiveMoveResizeAnchor()); @@ -4606,7 +4614,6 @@ void X11Window::maximize(MaximizeMode mode) } else { updateQuickTileMode(QuickTileFlag::None); } - setTile(nullptr); info->setState(NET::Max, NET::Max); break; } @@ -4617,7 +4624,6 @@ void X11Window::maximize(MaximizeMode mode) blockGeometryUpdates(false); updateAllowedActions(); updateWindowRules(Rules::MaximizeVert | Rules::MaximizeHoriz | Rules::Position | Rules::Size); - Q_EMIT quickTileModeChanged(); if (max_mode != old_mode) { Q_EMIT maximizedChanged(); diff --git a/src/x11window.h b/src/x11window.h index af70970f98..4dd1895684 100644 --- a/src/x11window.h +++ b/src/x11window.h @@ -348,6 +348,7 @@ protected: void doInteractiveResizeSync(const QRectF &rect) override; QSizeF resizeIncrements() const override; bool acceptsFocus() const override; + void doSetQuickTileMode() override; void moveResizeInternal(const QRectF &rect, MoveResizeMode mode) override; std::unique_ptr createItem(Item *parentItem) override; diff --git a/src/xdgshellwindow.cpp b/src/xdgshellwindow.cpp index da1c9aa15b..c74e9c105e 100644 --- a/src/xdgshellwindow.cpp +++ b/src/xdgshellwindow.cpp @@ -10,6 +10,8 @@ */ #include "xdgshellwindow.h" #include "core/output.h" +#include "effect/globals.h" +#include "utils/common.h" #if KWIN_BUILD_ACTIVITIES #include "activities.h" #endif @@ -18,6 +20,7 @@ #include "placement.h" #include "pointer_input.h" #include "tablet_input.h" +#include "tiles/tilemanager.h" #include "touch_input.h" #include "utils/subsurfacemonitor.h" #include "virtualdesktops.h" @@ -814,7 +817,7 @@ static Qt::Edges anchorsForQuickTileMode(QuickTileMode mode) void XdgToplevelWindow::doSetQuickTileMode() { - const Qt::Edges anchors = anchorsForQuickTileMode(quickTileMode()); + const Qt::Edges anchors = anchorsForQuickTileMode(m_requestedQuickTileMode); if (anchors & Qt::LeftEdge) { m_nextStates |= XdgToplevelInterface::State::TiledLeft; @@ -1034,6 +1037,26 @@ void XdgToplevelWindow::handleStatesAcknowledged(const XdgToplevelInterface::Sta updateFullScreenMode(states & XdgToplevelInterface::State::FullScreen); } + if (delta & (XdgToplevelInterface::State::TiledLeft | XdgToplevelInterface::State::TiledTop | XdgToplevelInterface::State::TiledRight | XdgToplevelInterface::State::TiledBottom)) { + QuickTileMode newTileMode = QuickTileFlag::None; + if (states & XdgToplevelInterface::State::TiledLeft) { + newTileMode |= QuickTileFlag::Left; + } + if (states & XdgToplevelInterface::State::TiledTop) { + newTileMode |= QuickTileFlag::Top; + } + if (states & XdgToplevelInterface::State::TiledRight) { + newTileMode |= QuickTileFlag::Right; + } + if (states & XdgToplevelInterface::State::TiledBottom) { + newTileMode |= QuickTileFlag::Bottom; + } + + if (newTileMode != quickTileMode()) { + setTile(workspace()->tileManager(output())->quickTile(newTileMode)); + } + } + m_acknowledgedStates = states; } @@ -1511,6 +1534,7 @@ void XdgToplevelWindow::maximize(MaximizeMode mode) return; } + const auto oldQuickTileMode = requestedQuickTileMode(); Q_EMIT maximizedAboutToChange(mode); m_requestedMaximizeMode = mode; @@ -1534,7 +1558,7 @@ void XdgToplevelWindow::maximize(MaximizeMode mode) setNoBorder(m_requestedMaximizeMode == MaximizeFull); } - if (quickTileMode() == QuickTileMode(QuickTileFlag::None)) { + if (oldQuickTileMode == QuickTileMode(QuickTileFlag::None)) { QRectF savedGeometry = geometryRestore(); if (!(oldMode & MaximizeVertical)) { savedGeometry.setTop(oldGeometry.top()); @@ -1585,25 +1609,18 @@ void XdgToplevelWindow::maximize(MaximizeMode mode) } } - const auto oldQuickTileMode = quickTileMode(); if (m_requestedMaximizeMode == MaximizeFull) { if (options->electricBorderMaximize()) { updateQuickTileMode(QuickTileFlag::Maximize); } else { updateQuickTileMode(QuickTileFlag::None); } - setTile(nullptr); } else { updateQuickTileMode(QuickTileFlag::None); } moveResize(geometry); - if (oldQuickTileMode != quickTileMode()) { - doSetQuickTileMode(); - Q_EMIT quickTileModeChanged(); - } - doSetMaximized(); }