Quick tiling double buffereing

The quicktileMode member now is just for the requested tile mode, base the "real" mode only on m_tile.

The requested tile mode is used for double buffering, to look and behave just like requestedMAximizeMode() which is updated immediately, but needs to acknowledge the configure request and render for quickTileMode() (and the right tile() instanced to be associated) to be updated accordingly
This commit is contained in:
Marco Martin 2024-04-15 12:18:09 +00:00
parent 36001e5ee0
commit 52b92904de
12 changed files with 228 additions and 99 deletions

View file

@ -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<quint32>());
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<quint32>());
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<quint32>());
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);

View file

@ -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<Output *> 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<quint32>());
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<quint32>());
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<quint32>());
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<quint32>());
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<quint32>());
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<QVariant>{shortcut});
QDBusConnection::sessionBus().asyncCall(msg);
QVERIFY(surfaceConfigureRequestedSpy.wait());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
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<quint32>());
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);

View file

@ -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;
}

View file

@ -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();

View file

@ -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();
}

View file

@ -75,6 +75,12 @@ TileManager::TileManager(Output *parent)
TileManager::~TileManager()
{
m_tearingDown = true;
}
bool TileManager::tearingDown() const
{
return m_tearingDown;
}
Output *TileManager::output() const

View file

@ -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<CustomTile> m_rootTile = nullptr;
std::unique_ptr<QuickRootTile> m_quickRootTile = nullptr;
std::unique_ptr<TileModel> m_tileModel = nullptr;
bool m_tearingDown = false;
friend class CustomTile;
};

View file

@ -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);

View file

@ -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

View file

@ -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();

View file

@ -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<WindowItem> createItem(Item *parentItem) override;

View file

@ -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();
}