Avoid moving the window while it's maximized

Unlike X11, on Wayland, the window won't change its maximize mode until
it renders a new buffer. This creates a problem for interactive move
because if it's not careful and moves the window while it's still effectively
maximized, it will look as if the window has leaked to other screens.

This change fixes the problem by making Window::handleInteractiveMoveResize()
avoid move() if the window needs to be unmaximized.

As a bonus, it also allows to unmaximize the windows that are maximized
along only one dimension by dragging them.

Unfortunately, tiling stuff still suffers from the same issue. In order
to fix it, Window::tile() has to be part of double buffered state, like
Window::maximizeMode().

BUG: 449105
BUG: 459218
CCBUG: 482085
This commit is contained in:
Vlad Zahorodnii 2024-03-10 13:42:53 +02:00
parent 3e4c2b3ec8
commit 11a5513e78
6 changed files with 379 additions and 13 deletions

View file

@ -702,6 +702,7 @@ std::unique_ptr<FractionalScaleV1> createFractionalScaleV1(KWayland::Client::Sur
std::unique_ptr<XdgToplevel> createXdgToplevelSurface(KWayland::Client::Surface *surface);
std::unique_ptr<XdgToplevel> createXdgToplevelSurface(KWayland::Client::Surface *surface, CreationSetup configureMode);
std::unique_ptr<XdgToplevel> createXdgToplevelSurface(KWayland::Client::Surface *surface, std::function<void(XdgToplevel *toplevel)> setup);
std::unique_ptr<XdgPositioner> createXdgPositioner();

View file

@ -1004,6 +1004,24 @@ std::unique_ptr<XdgToplevel> createXdgToplevelSurface(KWayland::Client::Surface
return xdgToplevel;
}
std::unique_ptr<XdgToplevel> createXdgToplevelSurface(KWayland::Client::Surface *surface, std::function<void(XdgToplevel *toplevel)> setup)
{
XdgShell *shell = s_waylandConnection.xdgShell;
if (!shell) {
qWarning() << "Could not create an xdg_toplevel surface because xdg_wm_base global is not bound";
return nullptr;
}
XdgSurface *xdgSurface = new XdgSurface(shell, surface);
std::unique_ptr<XdgToplevel> xdgToplevel = std::make_unique<XdgToplevel>(xdgSurface);
setup(xdgToplevel.get());
waitForConfigured(xdgSurface);
return xdgToplevel;
}
std::unique_ptr<XdgPositioner> createXdgPositioner()
{
XdgShell *shell = s_waylandConnection.xdgShell;

View file

@ -68,6 +68,10 @@ private Q_SLOTS:
void testMaximizedToFullscreen_data();
void testMaximizedToFullscreen();
void testSendMaximizedWindowToAnotherOutput();
void testInteractiveMoveUnmaximizeFull();
void testInteractiveMoveUnmaximizeInitiallyFull();
void testInteractiveMoveUnmaximizeHorizontal();
void testInteractiveMoveUnmaximizeVertical();
void testFullscreenMultipleOutputs();
void testHidden();
void testDesktopFileName();
@ -1795,6 +1799,314 @@ void TestXdgShellWindow::testSendMaximizedWindowToAnotherOutput()
QCOMPARE(window->output(), outputs[1]);
}
void TestXdgShellWindow::testInteractiveMoveUnmaximizeFull()
{
// This test verifies that a maximized xdg-toplevel is going to be properly unmaximized when it's dragged.
// Create the window.
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
// Wait for the compositor to send a configure event with the activated state.
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
QVERIFY(surfaceConfigureRequestedSpy.wait());
// Make the window maximized.
const QRectF originalGeometry = window->frameGeometry();
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
window->maximize(MaximizeFull);
QVERIFY(surfaceConfigureRequestedSpy.wait());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::red);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->maximizeMode(), MaximizeFull);
QCOMPARE(window->requestedMaximizeMode(), MaximizeFull);
// Start interactive move.
QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped);
QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished);
const qreal xOffset = 0.25;
const qreal yOffset = 0.5;
quint32 timestamp = 0;
Test::pointerMotion(QPointF(window->x() + window->width() * xOffset, window->y() + window->height() * yOffset), timestamp++);
window->performMouseCommand(Options::MouseMove, input()->pointer()->pos());
QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
QCOMPARE(window->maximizeMode(), MaximizeFull);
QCOMPARE(window->requestedMaximizeMode(), MaximizeFull);
// Move the window to unmaximize it.
const QRectF maximizedGeometry = window->frameGeometry();
Test::pointerMotionRelative(QPointF(0, 100), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 0);
QCOMPARE(window->maximizeMode(), MaximizeFull);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), maximizedGeometry);
// Move the window a tiny bit more.
Test::pointerMotionRelative(QPointF(0, 10), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 0);
QCOMPARE(window->maximizeMode(), MaximizeFull);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), maximizedGeometry);
// Render the window at the new size.
QVERIFY(surfaceConfigureRequestedSpy.wait());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::red);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->maximizeMode(), MaximizeRestore);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), QRectF(input()->pointer()->pos() - QPointF(originalGeometry.width() * xOffset, originalGeometry.height() * yOffset), originalGeometry.size()));
// Move the window again.
const QRectF normalGeometry = window->frameGeometry();
Test::pointerMotionRelative(QPointF(0, 10), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
QCOMPARE(window->maximizeMode(), MaximizeRestore);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), normalGeometry.translated(0, 10));
// Finish interactive move.
window->keyPressEvent(Qt::Key_Enter);
QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
}
void TestXdgShellWindow::testInteractiveMoveUnmaximizeInitiallyFull()
{
// This test verifies that an initially maximized xdg-toplevel will be properly unmaximized when it's dragged.
// Create the window.
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), [](Test::XdgToplevel *toplevel) {
toplevel->set_maximized();
}));
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
// Wait for the compositor to send a configure event with the activated state.
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
QVERIFY(surfaceConfigureRequestedSpy.wait());
// Start interactive move.
QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped);
QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished);
const qreal xOffset = 0.25;
const qreal yOffset = 0.5;
quint32 timestamp = 0;
Test::pointerMotion(QPointF(window->x() + window->width() * xOffset, window->y() + window->height() * yOffset), timestamp++);
window->performMouseCommand(Options::MouseMove, input()->pointer()->pos());
QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
QCOMPARE(window->maximizeMode(), MaximizeFull);
QCOMPARE(window->requestedMaximizeMode(), MaximizeFull);
// Move the window to unmaximize it.
const QRectF maximizedGeometry = window->frameGeometry();
Test::pointerMotionRelative(QPointF(0, 100), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 0);
QCOMPARE(window->maximizeMode(), MaximizeFull);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), maximizedGeometry);
// Move the window a tiny bit more.
Test::pointerMotionRelative(QPointF(0, 10), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 0);
QCOMPARE(window->maximizeMode(), MaximizeFull);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), maximizedGeometry);
// Render the window at the new size.
const QSize restoredSize(100, 50);
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), QSize(0, 0));
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), restoredSize, Qt::red);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->maximizeMode(), MaximizeRestore);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), QRectF(input()->pointer()->pos() - QPointF(restoredSize.width() * xOffset, restoredSize.height() * yOffset), restoredSize));
// Move the window again.
const QRectF normalGeometry = window->frameGeometry();
Test::pointerMotionRelative(QPointF(0, 10), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
QCOMPARE(window->maximizeMode(), MaximizeRestore);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), normalGeometry.translated(0, 10));
// Finish interactive move.
window->keyPressEvent(Qt::Key_Enter);
QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
}
void TestXdgShellWindow::testInteractiveMoveUnmaximizeHorizontal()
{
// This test verifies that a maximized horizontally xdg-toplevel is going to be properly unmaximized when it's dragged horizontally.
// Create the window.
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
// Wait for the compositor to send a configure event with the activated state.
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
QVERIFY(surfaceConfigureRequestedSpy.wait());
// Make the window maximized.
const QRectF originalGeometry = window->frameGeometry();
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
window->maximize(MaximizeHorizontal);
QVERIFY(surfaceConfigureRequestedSpy.wait());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::red);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->maximizeMode(), MaximizeHorizontal);
QCOMPARE(window->requestedMaximizeMode(), MaximizeHorizontal);
// Start interactive move.
QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped);
QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished);
const qreal xOffset = 0.25;
const qreal yOffset = 0.5;
quint32 timestamp = 0;
Test::pointerMotion(QPointF(window->x() + window->width() * xOffset, window->y() + window->height() * yOffset), timestamp++);
window->performMouseCommand(Options::MouseMove, input()->pointer()->pos());
QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
QCOMPARE(window->maximizeMode(), MaximizeHorizontal);
QCOMPARE(window->requestedMaximizeMode(), MaximizeHorizontal);
// Move the window vertically, it's not going to be unmaximized.
const QRectF maximizedGeometry = window->frameGeometry();
Test::pointerMotionRelative(QPointF(0, 100), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
QCOMPARE(window->maximizeMode(), MaximizeHorizontal);
QCOMPARE(window->requestedMaximizeMode(), MaximizeHorizontal);
QCOMPARE(window->frameGeometry(), maximizedGeometry.translated(0, 100));
// Move the window horizontally.
Test::pointerMotionRelative(QPointF(100, 0), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
QCOMPARE(window->maximizeMode(), MaximizeHorizontal);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), maximizedGeometry.translated(0, 100));
// Move the window to the right a bit more.
Test::pointerMotionRelative(QPointF(10, 0), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
QCOMPARE(window->maximizeMode(), MaximizeHorizontal);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), maximizedGeometry.translated(0, 100));
// Render the window at the new size.
QVERIFY(surfaceConfigureRequestedSpy.wait());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::red);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->maximizeMode(), MaximizeRestore);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), QRectF(input()->pointer()->pos() - QPointF(originalGeometry.width() * xOffset, originalGeometry.height() * yOffset), originalGeometry.size()));
// Move the window again.
const QRectF normalGeometry = window->frameGeometry();
Test::pointerMotionRelative(QPointF(10, 0), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 2);
QCOMPARE(window->maximizeMode(), MaximizeRestore);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), normalGeometry.translated(10, 0));
// Finish interactive move.
window->keyPressEvent(Qt::Key_Enter);
QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
}
void TestXdgShellWindow::testInteractiveMoveUnmaximizeVertical()
{
// This test verifies that a maximized vertically xdg-toplevel is going to be properly unmaximized when it's dragged vertically.
// Create the window.
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
// Wait for the compositor to send a configure event with the activated state.
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
QVERIFY(surfaceConfigureRequestedSpy.wait());
// Make the window maximized.
const QRectF originalGeometry = window->frameGeometry();
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
window->maximize(MaximizeVertical);
QVERIFY(surfaceConfigureRequestedSpy.wait());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::red);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->maximizeMode(), MaximizeVertical);
QCOMPARE(window->requestedMaximizeMode(), MaximizeVertical);
// Start interactive move.
QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped);
QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished);
const qreal xOffset = 0.25;
const qreal yOffset = 0.5;
quint32 timestamp = 0;
Test::pointerMotion(QPointF(window->x() + window->width() * xOffset, window->y() + window->height() * yOffset), timestamp++);
window->performMouseCommand(Options::MouseMove, input()->pointer()->pos());
QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
QCOMPARE(window->maximizeMode(), MaximizeVertical);
QCOMPARE(window->requestedMaximizeMode(), MaximizeVertical);
// Move the window to the right, it's not going to be unmaximized.
const QRectF maximizedGeometry = window->frameGeometry();
Test::pointerMotionRelative(QPointF(100, 0), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
QCOMPARE(window->maximizeMode(), MaximizeVertical);
QCOMPARE(window->requestedMaximizeMode(), MaximizeVertical);
QCOMPARE(window->frameGeometry(), maximizedGeometry.translated(100, 0));
// Move the window vertically.
Test::pointerMotionRelative(QPointF(0, 100), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
QCOMPARE(window->maximizeMode(), MaximizeVertical);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), maximizedGeometry.translated(100, 0));
// Move the window down a bit more.
Test::pointerMotionRelative(QPointF(0, 10), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
QCOMPARE(window->maximizeMode(), MaximizeVertical);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), maximizedGeometry.translated(100, 0));
// Render the window at the new size.
QVERIFY(surfaceConfigureRequestedSpy.wait());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::red);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->maximizeMode(), MaximizeRestore);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), QRectF(input()->pointer()->pos() - QPointF(originalGeometry.width() * xOffset, originalGeometry.height() * yOffset), originalGeometry.size()));
// Move the window again.
const QRectF normalGeometry = window->frameGeometry();
Test::pointerMotionRelative(QPointF(0, 10), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 2);
QCOMPARE(window->maximizeMode(), MaximizeRestore);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), normalGeometry.translated(0, 10));
// Finish interactive move.
window->keyPressEvent(Qt::Key_Enter);
QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
}
void TestXdgShellWindow::testMaximizeAndChangeDecorationModeAfterInitialCommit()
{
// Ideally, the app would initialize the xdg-toplevel surface before the initial commit, but

View file

@ -1450,16 +1450,24 @@ void Window::handleInteractiveMoveResize(const QPointF &local, const QPointF &gl
nextMoveResizeGeom = nextMoveGeometry();
if (nextMoveResizeGeom != currentMoveResizeGeom) {
GeometryUpdatesBlocker blocker(this);
if (!isRequestedFullScreen() && quickTileMode() != QuickTileMode(QuickTileFlag::None)) {
setQuickTileMode(QuickTileFlag::None);
const QRectF &geom_restore = geometryRestore();
if (rules()->checkMaximize(MaximizeRestore) == MaximizeRestore) {
setMoveResizeGeometry(geom_restore);
if (!isRequestedFullScreen()) {
if (maximizeMode() != MaximizeRestore) {
if (maximizeMode() & MaximizeHorizontal) {
if (nextMoveResizeGeom.x() != currentMoveResizeGeom.x() || nextMoveResizeGeom.width() != currentMoveResizeGeom.width()) {
maximize(MaximizeRestore);
return;
}
}
if (maximizeMode() & MaximizeVertical) {
if (nextMoveResizeGeom.y() != currentMoveResizeGeom.y() || nextMoveResizeGeom.height() != currentMoveResizeGeom.height()) {
maximize(MaximizeRestore);
return;
}
}
} else if (quickTileMode() != QuickTileMode(QuickTileFlag::None)) {
setQuickTileMode(QuickTileFlag::None);
return;
}
nextMoveResizeGeom = nextMoveGeometry(); // fix position
}
move(nextMoveResizeGeom.topLeft());
@ -3562,11 +3570,18 @@ void Window::setQuickTileMode(QuickTileMode mode, bool keyboard)
if (mode == QuickTileMode(QuickTileFlag::None)) {
setTile(nullptr);
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
moveResize(geometryRestore());
QRectF geometry = moveResizeGeometry();
if (geometryRestore().isValid()) {
geometry = geometryRestore();
}
checkWorkspacePosition(); // Just in case it's a different screen
if (isInteractiveMove()) {
const QPointF anchor = interactiveMoveResizeAnchor();
const QPointF offset = interactiveMoveOffset();
geometry.moveTopLeft(QPointF(anchor.x() - geometry.width() * offset.x(),
anchor.y() - geometry.height() * offset.y()));
}
moveResize(geometry);
} else if (mode == QuickTileMode(QuickTileFlag::Custom)) {
Tile *tile = nullptr;
if (keyboard) {

View file

@ -4583,6 +4583,15 @@ void X11Window::maximize(MaximizeMode mode)
}
restore.setSize(constrainFrameSize(restore.size(), SizeModeAny));
if (isInteractiveMove()) {
if (!isFullScreen()) {
const QPointF anchor = interactiveMoveResizeAnchor();
const QPointF offset = interactiveMoveOffset();
restore.moveTopLeft(QPointF(anchor.x() - offset.x() * restore.width(),
anchor.y() - offset.y() * restore.height()));
}
}
moveResize(restore);
info->setState(NET::States(), NET::Max);

View file

@ -217,6 +217,17 @@ void XdgSurfaceWindow::handleNextWindowGeometry()
// Both the compositor and the client can change the window geometry. If the client
// sets a new window geometry, the compositor's move-resize geometry will be invalid.
maybeUpdateMoveResizeGeometry(frameGeometry);
} else if (isInteractiveMove()) {
bool fullscreen = isFullScreen();
if (const auto configureEvent = static_cast<XdgToplevelConfigure *>(lastAcknowledgedConfigure())) {
fullscreen = configureEvent->states & XdgToplevelInterface::State::FullScreen;
}
if (!fullscreen) {
const QPointF anchor = interactiveMoveResizeAnchor();
const QPointF offset = interactiveMoveOffset();
frameGeometry.moveTopLeft(QPointF(anchor.x() - offset.x() * frameGeometry.width(),
anchor.y() - offset.y() * frameGeometry.height()));
}
}
updateGeometry(frameGeometry);