window: make setQuickTileMode more sane

...by removing the keyboard flag, moving keyboard shortcut handling into a separate
method and making the position to tile the window at explicit

This also means KWin doesn't do as many intermediary changes to window geometry on output
hotplugs, which may work around some clients not always reacting to no-op changes.

CCBUG: 479694
This commit is contained in:
Xaver Hugl 2024-07-12 16:50:22 +02:00
parent 16ff6d777a
commit 370c9c8953
7 changed files with 81 additions and 94 deletions

View file

@ -819,7 +819,7 @@ void MoveResizeWindowTest::testCancelInteractiveMoveResize()
if (maximizeMode) {
window->setMaximize(maximizeMode & MaximizeMode::MaximizeVertical, maximizeMode & MaximizeMode::MaximizeHorizontal);
} else {
window->setQuickTileMode(quickTileMode, true);
window->setQuickTileModeAtCurrentPosition(quickTileMode);
}
if (quickTileMode == QuickTileFlag::Maximize) {
QCOMPARE(window->requestedQuickTileMode(), QuickTileFlag::None);

View file

@ -491,7 +491,7 @@ void OutputChangesTest::testQuickTiledWindowRestoredAfterEnablingOutput()
// Move the window to the right monitor and tile it to the right.
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
window->move(QPointF(1280 + 50, 100));
window->setQuickTileMode(QuickTileFlag::Right, true);
window->setQuickTileModeAtCurrentPosition(QuickTileFlag::Right);
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), QSize(1280 / 2, 1024));
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
@ -587,7 +587,7 @@ void OutputChangesTest::testCustomTiledWindowRestoredAfterEnablingOutput()
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
window->move(customTileGeom.topLeft() + QPointF(50, 50));
const auto geomBeforeTiling = window->moveResizeGeometry();
window->setQuickTileMode(QuickTileFlag::Custom, true);
window->setQuickTileModeAtCurrentPosition(QuickTileFlag::Custom);
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), customTileGeom.size().toSize());

View file

@ -174,7 +174,7 @@ void QuickTilingTest::testQuickTiling()
QFETCH(QuickTileMode, mode);
QFETCH(QRectF, expectedGeometry);
const QuickTileMode oldQuickTileMode = window->quickTileMode();
window->setQuickTileMode(mode, true);
window->handleQuickTileShortcut(mode);
// at this point the geometry did not yet change
QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
@ -223,7 +223,7 @@ void QuickTilingTest::testQuickTiling()
QCOMPARE(window->tile(), mode == QuickTileFlag::Maximize ? nullptr : tile);
// now try to toggle again
window->setQuickTileMode(mode, true);
window->handleQuickTileShortcut(mode);
QTEST(window->requestedQuickTileMode(), "expectedModeAfterToggle");
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(surfaceConfigureRequestedSpy.count(), 3);
@ -278,7 +278,7 @@ void QuickTilingTest::testQuickMaximizing()
QSignalSpy maximizeChangedSpy(window, &Window::maximizedChanged);
const QuickTileMode oldQuickTileMode = window->quickTileMode();
window->setQuickTileMode(QuickTileFlag::Maximize, true);
window->setQuickTileModeAtCurrentPosition(QuickTileFlag::Maximize);
// at this point the geometry did not yet change
QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
@ -310,7 +310,7 @@ void QuickTilingTest::testQuickMaximizing()
// go back to quick tile none
QFETCH(QuickTileMode, mode);
window->setQuickTileMode(mode, true);
window->setQuickTileModeAtCurrentPosition(mode);
QCOMPARE(window->requestedQuickTileMode(), QuickTileMode(QuickTileFlag::None));
// geometry not yet changed
QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024));
@ -637,7 +637,7 @@ void QuickTilingTest::testX11QuickTiling()
QSignalSpy quickTileChangedSpy(window, &Window::quickTileModeChanged);
const QRectF origGeo = window->frameGeometry();
QFETCH(QuickTileMode, mode);
window->setQuickTileMode(mode, true);
window->handleQuickTileShortcut(mode);
if (mode == QuickTileFlag::Maximize) {
QCOMPARE(quickTileChangedSpy.count(), 0);
QCOMPARE(window->quickTileMode(), QuickTileFlag::None);
@ -652,7 +652,7 @@ void QuickTilingTest::testX11QuickTiling()
// if screen is on the same edge
const auto outputs = workspace()->outputs();
QCOMPARE(window->output(), outputs[0]);
window->setQuickTileMode(mode, true);
window->handleQuickTileShortcut(mode);
QFETCH(int, screenId);
QCOMPARE(window->output(), outputs[screenId]);
QTEST(window->quickTileMode(), "modeAfterToggle");
@ -728,7 +728,7 @@ void QuickTilingTest::testX11QuickTilingAfterVertMaximize()
// now quick tile
QSignalSpy quickTileChangedSpy(window, &Window::quickTileModeChanged);
QFETCH(QuickTileMode, mode);
window->setQuickTileMode(mode, true);
window->setQuickTileModeAtCurrentPosition(mode);
if (mode == QuickTileFlag::Maximize) {
QCOMPARE(window->quickTileMode(), QuickTileFlag::None);
QCOMPARE(quickTileChangedSpy.count(), 0);

View file

@ -872,7 +872,7 @@ void Workspace::quickTileWindow(QuickTileMode mode)
m_quickTileCombineTimer->stop();
}
m_activeWindow->setQuickTileMode(mode, true);
m_activeWindow->handleQuickTileShortcut(mode);
}
qreal Workspace::packPositionLeft(const Window *window, qreal oldX, bool leftEdge) const

View file

@ -115,19 +115,10 @@ void PlacementTracker::restore(const QString &key)
}
}
if (restore) {
// to work around setQuickTileMode having unexpected side effects, make sure the window isn't tiled before
// setting the desired quick tile mode
// TODO fix this more properly
window->setQuickTileMode(QuickTileFlag::None, true);
if (newData.quickTile != QuickTileFlag::Custom) {
window->setQuickTileMode(newData.quickTile, true);
}
window->setQuickTileMode(newData.quickTile, newData.geometry.center());
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);

View file

@ -1221,7 +1221,7 @@ bool Window::startInteractiveMoveResize()
}
if (isInteractiveResize() && m_tile && !m_tile->supportsResizeGravity(interactiveMoveResizeGravity())) {
setQuickTileMode(QuickTileFlag::None);
setQuickTileModeAtCurrentPosition(QuickTileFlag::None);
}
updateElectricGeometryRestore();
@ -1246,7 +1246,7 @@ void Window::finishInteractiveMoveResize(bool cancel)
setMaximize(m_interactiveMoveResize.initialMaximizeMode & MaximizeMode::MaximizeVertical, m_interactiveMoveResize.initialMaximizeMode & MaximizeMode::MaximizeHorizontal);
setGeometryRestore(m_interactiveMoveResize.initialGeometryRestore);
} else if (m_interactiveMoveResize.initialQuickTileMode) {
setQuickTileMode(m_interactiveMoveResize.initialQuickTileMode, true);
setQuickTileMode(m_interactiveMoveResize.initialQuickTileMode, m_interactiveMoveResize.initialGeometry.center());
setGeometryRestore(m_interactiveMoveResize.initialGeometryRestore);
}
} else if (moveResizeOutput() != interactiveMoveResizeStartOutput()) {
@ -1257,10 +1257,10 @@ void Window::finishInteractiveMoveResize(bool cancel)
}
if (isElectricBorderMaximizing()) {
setQuickTileMode(electricBorderMode());
setQuickTileMode(electricBorderMode(), m_interactiveMoveResize.anchor);
setElectricBorderMaximizing(false);
} else if (wasMove && (m_interactiveMoveResize.modifiers & Qt::ShiftModifier)) {
setQuickTileMode(QuickTileFlag::Custom);
setQuickTileMode(QuickTileFlag::Custom, m_interactiveMoveResize.anchor);
}
setElectricBorderMode(QuickTileMode(QuickTileFlag::None));
workspace()->outline()->hide();
@ -1403,7 +1403,7 @@ void Window::updateInteractiveMoveResize(const QPointF &global, Qt::KeyboardModi
}
}
} else if (quickTileMode() != QuickTileMode(QuickTileFlag::None)) {
setQuickTileMode(QuickTileFlag::None);
setQuickTileModeAtCurrentPosition(QuickTileFlag::None);
return;
}
}
@ -3454,64 +3454,17 @@ QRectF Window::quickTileGeometryRestore() const
}
}
void Window::setQuickTileMode(QuickTileMode mode, bool keyboard)
void Window::handleQuickTileShortcut(QuickTileMode mode)
{
// Only allow quick tile on a regular window.
if (!isResizable()) {
if (!isResizable() || isAppletPopup()) {
return;
}
if (isAppletPopup()) {
return;
}
workspace()->updateFocusMousePosition(Cursors::self()->mouse()->pos()); // may cause leave event
const QuickTileMode oldMode = requestedQuickTileMode();
QPointF whichScreen = keyboard ? moveResizeGeometry().center() : Cursors::self()->mouse()->pos();
if (mode == QuickTileMode(QuickTileFlag::Maximize)) {
if (requestedMaximizeMode() == MaximizeFull) {
m_requestedQuickTileMode = QuickTileFlag::None;
setMaximize(false, false);
} else {
QRectF effectiveGeometryRestore = quickTileGeometryRestore();
m_requestedQuickTileMode = QuickTileFlag::Maximize;
setMaximize(true, true);
setGeometryRestore(effectiveGeometryRestore);
}
doSetQuickTileMode();
return;
}
// sanitize the mode, ie. simplify "invalid" combinations
if ((mode & QuickTileFlag::Horizontal) == QuickTileMode(QuickTileFlag::Horizontal)) {
mode &= ~QuickTileMode(QuickTileFlag::Horizontal);
}
if ((mode & QuickTileFlag::Vertical) == QuickTileMode(QuickTileFlag::Vertical)) {
mode &= ~QuickTileMode(QuickTileFlag::Vertical);
}
// 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_requestedQuickTileMode = QuickTileFlag::None;
setMaximize(false, false);
moveResize(quickTileGeometry(mode, keyboard ? moveResizeGeometry().center() : Cursors::self()->mouse()->pos()));
// Store the mode change
m_requestedQuickTileMode = mode;
} else {
m_requestedQuickTileMode = mode;
setMaximize(false, false);
}
doSetQuickTileMode();
return;
}
if (mode != QuickTileMode(QuickTileFlag::None)) {
QPointF tileAtPoint = moveResizeGeometry().center();
if (mode != 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)
const QuickTileMode oldMode = requestedQuickTileMode();
if (oldMode == mode) {
Output *currentOutput = moveResizeOutput();
Output *nextOutput = currentOutput;
@ -3535,8 +3488,7 @@ void Window::setQuickTileMode(QuickTileMode mode, bool keyboard)
mode = QuickTileFlag::None; // No other screens in the tile direction, toggle tiling
} else {
// Move to other screen
moveResize(geometryRestore().translated(nextOutput->geometry().topLeft() - currentOutput->geometry().topLeft()));
whichScreen = nextOutput->geometry().center();
tileAtPoint = nextOutput->geometry().center();
// Swap sides
if (shiftHorizontal) {
@ -3546,17 +3498,66 @@ void Window::setQuickTileMode(QuickTileMode mode, bool keyboard)
mode = (~mode & QuickTileFlag::Vertical) | (mode & QuickTileFlag::Horizontal);
}
}
} else if (oldMode == QuickTileMode(QuickTileFlag::None)) {
} else if (oldMode == QuickTileMode(QuickTileFlag::None) && requestedMaximizeMode() == MaximizeMode::MaximizeRestore) {
// 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_requestedQuickTileMode = mode;
}
setQuickTileMode(mode, tileAtPoint);
}
if (mode == QuickTileMode(QuickTileFlag::None)) {
m_requestedQuickTileMode = QuickTileFlag::None;
void Window::setQuickTileModeAtCurrentPosition(QuickTileMode mode)
{
setQuickTileMode(mode, m_moveResizeGeometry.center());
}
void Window::setQuickTileMode(QuickTileMode mode, const QPointF &tileAtPoint)
{
// Only allow quick tile on a regular window.
if (!isResizable() || isAppletPopup()) {
return;
}
workspace()->updateFocusMousePosition(Cursors::self()->mouse()->pos()); // may cause leave event
const QuickTileMode oldMode = requestedQuickTileMode();
if (mode == QuickTileMode(QuickTileFlag::Maximize)) {
if (requestedMaximizeMode() == MaximizeFull) {
m_requestedQuickTileMode = QuickTileFlag::None;
setMaximize(false, false);
} else {
const QRectF effectiveGeometryRestore = quickTileGeometryRestore();
m_requestedQuickTileMode = QuickTileFlag::Maximize;
setMaximize(true, true);
setGeometryRestore(effectiveGeometryRestore);
}
doSetQuickTileMode();
return;
}
// sanitize the mode, ie. simplify "invalid" combinations
if ((mode & QuickTileFlag::Horizontal) == QuickTileMode(QuickTileFlag::Horizontal)) {
mode &= ~QuickTileMode(QuickTileFlag::Horizontal);
}
if ((mode & QuickTileFlag::Vertical) == QuickTileMode(QuickTileFlag::Vertical)) {
mode &= ~QuickTileMode(QuickTileFlag::Vertical);
}
m_requestedQuickTileMode = mode;
// restore from maximized so that it is possible to tile maximized windows with one hit or by dragging
if (requestedMaximizeMode() != MaximizeRestore) {
m_requestedQuickTileMode = QuickTileFlag::None;
setMaximize(false, false);
setQuickTileMode(mode, tileAtPoint);
return;
}
if (oldMode == QuickTileFlag::None) {
setGeometryRestore(quickTileGeometryRestore());
}
if (mode == QuickTileMode(QuickTileFlag::None)) {
QRectF geometry = moveResizeGeometry();
if (geometryRestore().isValid()) {
geometry = geometryRestore();
@ -3575,12 +3576,11 @@ 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
m_requestedQuickTileMode = mode;
setTile(workspace()->tileManager(workspace()->outputAt(whichScreen))->bestTileForPosition(whichScreen));
setTile(workspace()->tileManager(workspace()->outputAt(tileAtPoint))->bestTileForPosition(tileAtPoint));
// Don't go into setTileMode as custom tiles don't go trough configure events
return;
} else {
Tile *newTile = workspace()->tileManager(workspace()->outputAt(whichScreen))->quickTile(m_requestedQuickTileMode);
Tile *newTile = workspace()->tileManager(workspace()->outputAt(tileAtPoint))->quickTile(m_requestedQuickTileMode);
if (newTile) {
moveResize(newTile->absoluteGeometry());
} else if (tile()) {

View file

@ -1090,13 +1090,9 @@ public:
Tile *tile() const;
void setTile(Tile *tile);
/**
* Sets the quick tile mode ("snap") of this window.
* This will also handle preserving and restoring of window geometry as necessary.
* @param mode The tile mode (left/right) to give this window.
* @param keyboard Defines whether to take keyboard cursor into account.
*/
void setQuickTileMode(QuickTileMode mode, bool keyboard = false);
void handleQuickTileShortcut(QuickTileMode mode);
void setQuickTileModeAtCurrentPosition(QuickTileMode mode);
void setQuickTileMode(QuickTileMode mode, const QPointF &tileAtPoint);
QuickTileMode quickTileMode() const;
QuickTileMode requestedQuickTileMode() const;