wayland: Implement maximized horizontal/vertical states

BUG: 407793
This commit is contained in:
Vlad Zahorodnii 2020-09-10 13:59:56 +03:00
parent dd1b45d828
commit 378ecbc88c
2 changed files with 306 additions and 20 deletions

View file

@ -72,6 +72,9 @@ private Q_SLOTS:
void testUserCanSetFullscreen(); void testUserCanSetFullscreen();
void testUserSetFullscreen(); void testUserSetFullscreen();
void testMaximizeHorizontal();
void testMaximizeVertical();
void testMaximizeFull();
void testMaximizedToFullscreen_data(); void testMaximizedToFullscreen_data();
void testMaximizedToFullscreen(); void testMaximizedToFullscreen();
void testFullscreenMultipleOutputs(); void testFullscreenMultipleOutputs();
@ -1698,5 +1701,248 @@ void TestXdgShellClient::testDoubleMaximize()
QVERIFY(states.testFlag(XdgShellSurface::State::Maximized)); QVERIFY(states.testFlag(XdgShellSurface::State::Maximized));
} }
void TestXdgShellClient::testMaximizeHorizontal()
{
// Create the test client.
QScopedPointer<Surface> surface;
surface.reset(Test::createSurface());
QScopedPointer<XdgShellSurface> shellSurface;
shellSurface.reset(Test::createXdgShellStableSurface(surface.data(), surface.data(), Test::CreationSetup::CreateOnly));
QScopedPointer<QSignalSpy> configureRequestedSpy;
configureRequestedSpy.reset(new QSignalSpy(shellSurface.data(), &XdgShellSurface::configureRequested));
surface->commit(Surface::CommitFlag::None);
// Wait for the initial configure event.
XdgShellSurface::States states;
QVERIFY(configureRequestedSpy->wait());
QCOMPARE(configureRequestedSpy->count(), 1);
QCOMPARE(configureRequestedSpy->last().at(0).toSize(), QSize(0, 0));
states = configureRequestedSpy->last().at(1).value<XdgShellSurface::States>();
QVERIFY(!states.testFlag(XdgShellSurface::State::Activated));
QVERIFY(!states.testFlag(XdgShellSurface::State::Maximized));
// Map the client.
shellSurface->ackConfigure(configureRequestedSpy->last().at(2).value<quint32>());
AbstractClient *client = Test::renderAndWaitForShown(surface.data(), QSize(800, 600), Qt::blue);
QVERIFY(client);
QVERIFY(client->isActive());
QVERIFY(client->isMaximizable());
QCOMPARE(client->maximizeMode(), MaximizeMode::MaximizeRestore);
QCOMPARE(client->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
QCOMPARE(client->size(), QSize(800, 600));
// We should receive a configure event when the client becomes active.
QVERIFY(configureRequestedSpy->wait());
QCOMPARE(configureRequestedSpy->count(), 2);
states = configureRequestedSpy->last().at(1).value<XdgShellSurface::States>();
QVERIFY(states.testFlag(XdgShellSurface::State::Activated));
QVERIFY(!states.testFlag(XdgShellSurface::State::Maximized));
// Maximize the test client in horizontal direction.
workspace()->slotWindowMaximizeHorizontal();
QCOMPARE(client->requestedMaximizeMode(), MaximizeHorizontal);
QCOMPARE(client->maximizeMode(), MaximizeRestore);
QVERIFY(configureRequestedSpy->wait());
QCOMPARE(configureRequestedSpy->count(), 3);
QCOMPARE(configureRequestedSpy->last().at(0).toSize(), QSize(1280, 600));
states = configureRequestedSpy->last().at(1).value<XdgShellSurface::States>();
QVERIFY(!states.testFlag(XdgShellSurface::State::Maximized));
// Draw contents of the maximized client.
QSignalSpy geometryChangedSpy(client, &AbstractClient::geometryChanged);
QVERIFY(geometryChangedSpy.isValid());
shellSurface->ackConfigure(configureRequestedSpy->last().at(2).value<quint32>());
Test::render(surface.data(), QSize(1280, 600), Qt::blue);
QVERIFY(geometryChangedSpy.wait());
QCOMPARE(client->size(), QSize(1280, 600));
QCOMPARE(client->requestedMaximizeMode(), MaximizeHorizontal);
QCOMPARE(client->maximizeMode(), MaximizeHorizontal);
// Restore the client.
workspace()->slotWindowMaximizeHorizontal();
QCOMPARE(client->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(client->maximizeMode(), MaximizeHorizontal);
QVERIFY(configureRequestedSpy->wait());
QCOMPARE(configureRequestedSpy->count(), 4);
QCOMPARE(configureRequestedSpy->last().at(0).toSize(), QSize(800, 600));
states = configureRequestedSpy->last().at(1).value<XdgShellSurface::States>();
QVERIFY(!states.testFlag(XdgShellSurface::State::Maximized));
// Draw contents of the restored client.
shellSurface->ackConfigure(configureRequestedSpy->last().at(2).value<quint32>());
Test::render(surface.data(), QSize(800, 600), Qt::blue);
QVERIFY(geometryChangedSpy.wait());
QCOMPARE(client->size(), QSize(800, 600));
QCOMPARE(client->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(client->maximizeMode(), MaximizeRestore);
// Destroy the client.
shellSurface.reset();
surface.reset();
QVERIFY(Test::waitForWindowDestroyed(client));
}
void TestXdgShellClient::testMaximizeVertical()
{
// Create the test client.
QScopedPointer<Surface> surface;
surface.reset(Test::createSurface());
QScopedPointer<XdgShellSurface> shellSurface;
shellSurface.reset(Test::createXdgShellStableSurface(surface.data(), surface.data(), Test::CreationSetup::CreateOnly));
QScopedPointer<QSignalSpy> configureRequestedSpy;
configureRequestedSpy.reset(new QSignalSpy(shellSurface.data(), &XdgShellSurface::configureRequested));
surface->commit(Surface::CommitFlag::None);
// Wait for the initial configure event.
XdgShellSurface::States states;
QVERIFY(configureRequestedSpy->wait());
QCOMPARE(configureRequestedSpy->count(), 1);
QCOMPARE(configureRequestedSpy->last().at(0).toSize(), QSize(0, 0));
states = configureRequestedSpy->last().at(1).value<XdgShellSurface::States>();
QVERIFY(!states.testFlag(XdgShellSurface::State::Activated));
QVERIFY(!states.testFlag(XdgShellSurface::State::Maximized));
// Map the client.
shellSurface->ackConfigure(configureRequestedSpy->last().at(2).value<quint32>());
AbstractClient *client = Test::renderAndWaitForShown(surface.data(), QSize(800, 600), Qt::blue);
QVERIFY(client);
QVERIFY(client->isActive());
QVERIFY(client->isMaximizable());
QCOMPARE(client->maximizeMode(), MaximizeMode::MaximizeRestore);
QCOMPARE(client->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
QCOMPARE(client->size(), QSize(800, 600));
// We should receive a configure event when the client becomes active.
QVERIFY(configureRequestedSpy->wait());
QCOMPARE(configureRequestedSpy->count(), 2);
states = configureRequestedSpy->last().at(1).value<XdgShellSurface::States>();
QVERIFY(states.testFlag(XdgShellSurface::State::Activated));
QVERIFY(!states.testFlag(XdgShellSurface::State::Maximized));
// Maximize the test client in vertical direction.
workspace()->slotWindowMaximizeVertical();
QCOMPARE(client->requestedMaximizeMode(), MaximizeVertical);
QCOMPARE(client->maximizeMode(), MaximizeRestore);
QVERIFY(configureRequestedSpy->wait());
QCOMPARE(configureRequestedSpy->count(), 3);
QCOMPARE(configureRequestedSpy->last().at(0).toSize(), QSize(800, 1024));
states = configureRequestedSpy->last().at(1).value<XdgShellSurface::States>();
QVERIFY(!states.testFlag(XdgShellSurface::State::Maximized));
// Draw contents of the maximized client.
QSignalSpy geometryChangedSpy(client, &AbstractClient::geometryChanged);
QVERIFY(geometryChangedSpy.isValid());
shellSurface->ackConfigure(configureRequestedSpy->last().at(2).value<quint32>());
Test::render(surface.data(), QSize(800, 1024), Qt::blue);
QVERIFY(geometryChangedSpy.wait());
QCOMPARE(client->size(), QSize(800, 1024));
QCOMPARE(client->requestedMaximizeMode(), MaximizeVertical);
QCOMPARE(client->maximizeMode(), MaximizeVertical);
// Restore the client.
workspace()->slotWindowMaximizeVertical();
QCOMPARE(client->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(client->maximizeMode(), MaximizeVertical);
QVERIFY(configureRequestedSpy->wait());
QCOMPARE(configureRequestedSpy->count(), 4);
QCOMPARE(configureRequestedSpy->last().at(0).toSize(), QSize(800, 600));
states = configureRequestedSpy->last().at(1).value<XdgShellSurface::States>();
QVERIFY(!states.testFlag(XdgShellSurface::State::Maximized));
// Draw contents of the restored client.
shellSurface->ackConfigure(configureRequestedSpy->last().at(2).value<quint32>());
Test::render(surface.data(), QSize(800, 600), Qt::blue);
QVERIFY(geometryChangedSpy.wait());
QCOMPARE(client->size(), QSize(800, 600));
QCOMPARE(client->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(client->maximizeMode(), MaximizeRestore);
// Destroy the client.
shellSurface.reset();
surface.reset();
QVERIFY(Test::waitForWindowDestroyed(client));
}
void TestXdgShellClient::testMaximizeFull()
{
// Create the test client.
QScopedPointer<Surface> surface;
surface.reset(Test::createSurface());
QScopedPointer<XdgShellSurface> shellSurface;
shellSurface.reset(Test::createXdgShellStableSurface(surface.data(), surface.data(), Test::CreationSetup::CreateOnly));
QScopedPointer<QSignalSpy> configureRequestedSpy;
configureRequestedSpy.reset(new QSignalSpy(shellSurface.data(), &XdgShellSurface::configureRequested));
surface->commit(Surface::CommitFlag::None);
// Wait for the initial configure event.
XdgShellSurface::States states;
QVERIFY(configureRequestedSpy->wait());
QCOMPARE(configureRequestedSpy->count(), 1);
QCOMPARE(configureRequestedSpy->last().at(0).toSize(), QSize(0, 0));
states = configureRequestedSpy->last().at(1).value<XdgShellSurface::States>();
QVERIFY(!states.testFlag(XdgShellSurface::State::Activated));
QVERIFY(!states.testFlag(XdgShellSurface::State::Maximized));
// Map the client.
shellSurface->ackConfigure(configureRequestedSpy->last().at(2).value<quint32>());
AbstractClient *client = Test::renderAndWaitForShown(surface.data(), QSize(800, 600), Qt::blue);
QVERIFY(client);
QVERIFY(client->isActive());
QVERIFY(client->isMaximizable());
QCOMPARE(client->maximizeMode(), MaximizeMode::MaximizeRestore);
QCOMPARE(client->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
QCOMPARE(client->size(), QSize(800, 600));
// We should receive a configure event when the client becomes active.
QVERIFY(configureRequestedSpy->wait());
QCOMPARE(configureRequestedSpy->count(), 2);
states = configureRequestedSpy->last().at(1).value<XdgShellSurface::States>();
QVERIFY(states.testFlag(XdgShellSurface::State::Activated));
QVERIFY(!states.testFlag(XdgShellSurface::State::Maximized));
// Maximize the test client.
workspace()->slotWindowMaximize();
QCOMPARE(client->requestedMaximizeMode(), MaximizeFull);
QCOMPARE(client->maximizeMode(), MaximizeRestore);
QVERIFY(configureRequestedSpy->wait());
QCOMPARE(configureRequestedSpy->count(), 3);
QCOMPARE(configureRequestedSpy->last().at(0).toSize(), QSize(1280, 1024));
states = configureRequestedSpy->last().at(1).value<XdgShellSurface::States>();
QVERIFY(states.testFlag(XdgShellSurface::State::Maximized));
// Draw contents of the maximized client.
QSignalSpy geometryChangedSpy(client, &AbstractClient::geometryChanged);
QVERIFY(geometryChangedSpy.isValid());
shellSurface->ackConfigure(configureRequestedSpy->last().at(2).value<quint32>());
Test::render(surface.data(), QSize(1280, 1024), Qt::blue);
QVERIFY(geometryChangedSpy.wait());
QCOMPARE(client->size(), QSize(1280, 1024));
QCOMPARE(client->requestedMaximizeMode(), MaximizeFull);
QCOMPARE(client->maximizeMode(), MaximizeFull);
// Restore the client.
workspace()->slotWindowMaximize();
QCOMPARE(client->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(client->maximizeMode(), MaximizeFull);
QVERIFY(configureRequestedSpy->wait());
QCOMPARE(configureRequestedSpy->count(), 4);
QCOMPARE(configureRequestedSpy->last().at(0).toSize(), QSize(800, 600));
states = configureRequestedSpy->last().at(1).value<XdgShellSurface::States>();
QVERIFY(!states.testFlag(XdgShellSurface::State::Maximized));
// Draw contents of the restored client.
shellSurface->ackConfigure(configureRequestedSpy->last().at(2).value<quint32>());
Test::render(surface.data(), QSize(800, 600), Qt::blue);
QVERIFY(geometryChangedSpy.wait());
QCOMPARE(client->size(), QSize(800, 600));
QCOMPARE(client->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(client->maximizeMode(), MaximizeRestore);
// Destroy the client.
shellSurface.reset();
surface.reset();
QVERIFY(Test::waitForWindowDestroyed(client));
}
WAYLANDTEST_MAIN(TestXdgShellClient) WAYLANDTEST_MAIN(TestXdgShellClient)
#include "xdgshellclient_test.moc" #include "xdgshellclient_test.moc"

View file

@ -1644,6 +1644,19 @@ void XdgToplevelClient::changeMaximize(bool horizontal, bool vertical, bool adju
changeMaximizeRecursion = false; changeMaximizeRecursion = false;
} }
if (quickTileMode() == QuickTileMode(QuickTileFlag::None)) {
QRect savedGeometry = geometryRestore();
if (!adjust && !(oldMode & MaximizeVertical)) {
savedGeometry.setTop(oldGeometry.top());
savedGeometry.setBottom(oldGeometry.bottom());
}
if (!adjust && !(oldMode & MaximizeHorizontal)) {
savedGeometry.setLeft(oldGeometry.left());
savedGeometry.setRight(oldGeometry.right());
}
setGeometryRestore(savedGeometry);
}
// Conditional quick tiling exit points // Conditional quick tiling exit points
const auto oldQuickTileMode = quickTileMode(); const auto oldQuickTileMode = quickTileMode();
if (quickTileMode() != QuickTileMode(QuickTileFlag::None)) { if (quickTileMode() != QuickTileMode(QuickTileFlag::None)) {
@ -1659,33 +1672,60 @@ void XdgToplevelClient::changeMaximize(bool horizontal, bool vertical, bool adju
} }
} }
const MaximizeMode delta = m_requestedMaximizeMode ^ oldMode;
QRect geometry = oldGeometry;
if (adjust || (delta & MaximizeHorizontal)) {
if (m_requestedMaximizeMode & MaximizeHorizontal) {
// Stretch the window vertically to fit the size of the maximize area.
geometry.setX(clientArea.x());
geometry.setWidth(clientArea.width());
} else if (geometryRestore().isValid()) {
// The window is no longer maximized horizontally and the saved geometry is valid.
geometry.setX(geometryRestore().x());
geometry.setWidth(geometryRestore().width());
} else {
// The window is no longer maximized horizontally and the saved geometry is
// invalid. This would happen if the window had been mapped in the maximized state.
// We ask the client to resize the window horizontally to its preferred size.
geometry.setX(clientArea.x());
geometry.setWidth(0);
}
}
if (adjust || (delta & MaximizeVertical)) {
if (m_requestedMaximizeMode & MaximizeVertical) {
// Stretch the window horizontally to fit the size of the maximize area.
geometry.setY(clientArea.y());
geometry.setHeight(clientArea.height());
} else if (geometryRestore().isValid()) {
// The window is no longer maximized vertically and the saved geometry is valid.
geometry.setY(geometryRestore().y());
geometry.setHeight(geometryRestore().height());
} else {
// The window is no longer maximized vertically and the saved geometry is
// invalid. This would happen if the window had been mapped in the maximized state.
// We ask the client to resize the window vertically to its preferred size.
geometry.setY(clientArea.y());
geometry.setHeight(0);
}
}
if (m_requestedMaximizeMode == MaximizeFull) { if (m_requestedMaximizeMode == MaximizeFull) {
setGeometryRestore(oldGeometry);
// TODO: Client has more checks
if (options->electricBorderMaximize()) { if (options->electricBorderMaximize()) {
updateQuickTileMode(QuickTileFlag::Maximize); updateQuickTileMode(QuickTileFlag::Maximize);
} else { } else {
updateQuickTileMode(QuickTileFlag::None); updateQuickTileMode(QuickTileFlag::None);
} }
if (quickTileMode() != oldQuickTileMode) { } else if (m_requestedMaximizeMode == MaximizeRestore) {
doSetQuickTileMode(); updateQuickTileMode(QuickTileFlag::None);
emit quickTileModeChanged(); }
}
setFrameGeometry(workspace()->clientArea(MaximizeArea, this));
} else {
if (m_requestedMaximizeMode == MaximizeRestore) {
updateQuickTileMode(QuickTileFlag::None);
}
if (quickTileMode() != oldQuickTileMode) {
doSetQuickTileMode();
emit quickTileModeChanged();
}
if (geometryRestore().isValid()) { setFrameGeometry(geometry);
setFrameGeometry(geometryRestore());
} else { if (oldQuickTileMode != quickTileMode()) {
setFrameGeometry(workspace()->clientArea(PlacementArea, this)); doSetQuickTileMode();
} emit quickTileModeChanged();
} }
doSetMaximized(); doSetMaximized();