diff --git a/autotests/integration/quick_tiling_test.cpp b/autotests/integration/quick_tiling_test.cpp index 5e54977d4a..2cb1c74549 100644 --- a/autotests/integration/quick_tiling_test.cpp +++ b/autotests/integration/quick_tiling_test.cpp @@ -709,21 +709,25 @@ void QuickTilingTest::testX11QuickTilingAfterVertMaximize() void QuickTilingTest::testShortcut_data() { - QTest::addColumn("shortcut"); + QTest::addColumn("shortcutList"); QTest::addColumn("expectedMode"); QTest::addColumn("expectedGeometry"); #define FLAG(name) QuickTileMode(QuickTileFlag::name) - QTest::newRow("top") << QStringLiteral("Window Quick Tile Top") << FLAG(Top) << QRect(0, 0, 1280, 512); - QTest::newRow("left") << QStringLiteral("Window Quick Tile Left") << FLAG(Left) << QRect(0, 0, 640, 1024); - QTest::newRow("bottom") << QStringLiteral("Window Quick Tile Bottom") << FLAG(Bottom) << QRect(0, 512, 1280, 512); - QTest::newRow("right") << QStringLiteral("Window Quick Tile Right") << FLAG(Right) << QRect(640, 0, 640, 1024); - - QTest::newRow("top right") << QStringLiteral("Window Quick Tile Top Right") << (FLAG(Top) | FLAG(Right)) << QRect(640, 0, 640, 512); - QTest::newRow("top left") << QStringLiteral("Window Quick Tile Top Left") << (FLAG(Top) | FLAG(Left)) << QRect(0, 0, 640, 512); - QTest::newRow("bottom right") << QStringLiteral("Window Quick Tile Bottom Right") << (FLAG(Bottom) | FLAG(Right)) << QRect(640, 512, 640, 512); - QTest::newRow("bottom left") << QStringLiteral("Window Quick Tile Bottom Left") << (FLAG(Bottom) | FLAG(Left)) << QRect(0, 512, 640, 512); + QTest::newRow("top") << QStringList{QStringLiteral("Window Quick Tile Top")} << FLAG(Top) << QRect(0, 0, 1280, 512); + QTest::newRow("bottom") << QStringList{QStringLiteral("Window Quick Tile Bottom")} << FLAG(Bottom) << QRect(0, 512, 1280, 512); + QTest::newRow("top right") << QStringList{QStringLiteral("Window Quick Tile Top Right")} << (FLAG(Top) | FLAG(Right)) << QRect(640, 0, 640, 512); + QTest::newRow("top left") << QStringList{QStringLiteral("Window Quick Tile Top Left")} << (FLAG(Top) | FLAG(Left)) << QRect(0, 0, 640, 512); + QTest::newRow("bottom right") << QStringList{QStringLiteral("Window Quick Tile Bottom Right")} << (FLAG(Bottom) | FLAG(Right)) << QRect(640, 512, 640, 512); + QTest::newRow("bottom left") << QStringList{QStringLiteral("Window Quick Tile Bottom Left")} << (FLAG(Bottom) | FLAG(Left)) << QRect(0, 512, 640, 512); + QTest::newRow("left") << QStringList{QStringLiteral("Window Quick Tile Left")} << FLAG(Left) << QRect(0, 0, 640, 1024); + QTest::newRow("right") << QStringList{QStringLiteral("Window Quick Tile Right")} << FLAG(Right) << QRect(640, 0, 640, 1024); + // Test combined actions for corner tiling + QTest::newRow("top left combined") << QStringList{QStringLiteral("Window Quick Tile Left"), QStringLiteral("Window Quick Tile Top")} << (FLAG(Top) | FLAG(Left)) << QRect(0, 0, 640, 512); + QTest::newRow("top right combined") << QStringList{QStringLiteral("Window Quick Tile Right"), QStringLiteral("Window Quick Tile Top")} << (FLAG(Top) | FLAG(Right)) << QRect(640, 0, 640, 512); + QTest::newRow("bottom left combined") << QStringList{QStringLiteral("Window Quick Tile Left"), QStringLiteral("Window Quick Tile Bottom")} << (FLAG(Bottom) | FLAG(Left)) << QRect(0, 512, 640, 512); + QTest::newRow("bottom right combined") << QStringList{QStringLiteral("Window Quick Tile Right"), QStringLiteral("Window Quick Tile Bottom")} << (FLAG(Bottom) | FLAG(Right)) << QRect(640, 512, 640, 512); #undef FLAG } @@ -749,22 +753,30 @@ void QuickTilingTest::testShortcut() QVERIFY(configureRequestedSpy.wait()); QCOMPARE(configureRequestedSpy.count(), 1); - QFETCH(QString, shortcut); + QFETCH(QStringList, shortcutList); QFETCH(QRect, expectedGeometry); - // invoke global shortcut through dbus - auto msg = QDBusMessage::createMethodCall( - QStringLiteral("org.kde.kglobalaccel"), - QStringLiteral("/component/kwin"), - QStringLiteral("org.kde.kglobalaccel.Component"), - QStringLiteral("invokeShortcut")); - msg.setArguments(QList{shortcut}); - QDBusConnection::sessionBus().asyncCall(msg); + const int numberOfQuickTileActions = shortcutList.count(); + + if (numberOfQuickTileActions > 1) { + QTest::qWait(1001); + } + + for (QString shortcut : shortcutList) { + // invoke global shortcut through dbus + auto msg = QDBusMessage::createMethodCall( + QStringLiteral("org.kde.kglobalaccel"), + QStringLiteral("/component/kwin"), + QStringLiteral("org.kde.kglobalaccel.Component"), + QStringLiteral("invokeShortcut")); + msg.setArguments(QList{shortcut}); + QDBusConnection::sessionBus().asyncCall(msg); + } QSignalSpy quickTileChangedSpy(c, &AbstractClient::quickTileModeChanged); QVERIFY(quickTileChangedSpy.isValid()); QVERIFY(quickTileChangedSpy.wait()); - QCOMPARE(quickTileChangedSpy.count(), 1); + QCOMPARE(quickTileChangedSpy.count(), numberOfQuickTileActions); // at this point the geometry did not yet change QCOMPARE(c->frameGeometry(), QRect(0, 0, 100, 50)); // but quick tile mode already changed @@ -795,15 +807,13 @@ void QuickTilingTest::testScript_data() #define FLAG(name) QuickTileMode(QuickTileFlag::name) QTest::newRow("top") << QStringLiteral("Top") << FLAG(Top) << QRect(0, 0, 1280, 512); - QTest::newRow("left") << QStringLiteral("Left") << FLAG(Left) << QRect(0, 0, 640, 1024); QTest::newRow("bottom") << QStringLiteral("Bottom") << FLAG(Bottom) << QRect(0, 512, 1280, 512); - QTest::newRow("right") << QStringLiteral("Right") << FLAG(Right) << QRect(640, 0, 640, 1024); - QTest::newRow("top right") << QStringLiteral("TopRight") << (FLAG(Top) | FLAG(Right)) << QRect(640, 0, 640, 512); QTest::newRow("top left") << QStringLiteral("TopLeft") << (FLAG(Top) | FLAG(Left)) << QRect(0, 0, 640, 512); QTest::newRow("bottom right") << QStringLiteral("BottomRight") << (FLAG(Bottom) | FLAG(Right)) << QRect(640, 512, 640, 512); QTest::newRow("bottom left") << QStringLiteral("BottomLeft") << (FLAG(Bottom) | FLAG(Left)) << QRect(0, 512, 640, 512); - + QTest::newRow("left") << QStringLiteral("Left") << FLAG(Left) << QRect(0, 0, 640, 1024); + QTest::newRow("right") << QStringLiteral("Right") << FLAG(Right) << QRect(640, 0, 640, 1024); #undef FLAG } diff --git a/placement.cpp b/placement.cpp index f15860f525..a0fe02ac83 100644 --- a/placement.cpp +++ b/placement.cpp @@ -33,6 +33,7 @@ along with this program. If not, see . #include #include +#include namespace KWin { @@ -837,6 +838,24 @@ void Workspace::quickTileWindow(QuickTileMode mode) return; } + // If the user invokes two of these commands in a one second period, try to + // combine them together to enable easy and intuitive corner tiling +#define FLAG(name) QuickTileMode(QuickTileFlag::name) + if (!m_quickTileCombineTimer->isActive()) { + m_quickTileCombineTimer->start(1000); + m_lastTilingMode = mode; + } else { + if ( + ( (m_lastTilingMode == FLAG(Left) || m_lastTilingMode == FLAG(Right)) && (mode == FLAG(Top) || mode == FLAG(Bottom)) ) + || + ( (m_lastTilingMode == FLAG(Top) || m_lastTilingMode == FLAG(Bottom)) && (mode == FLAG(Left) || mode == FLAG(Right)) ) +#undef FLAG + ) { + mode |= m_lastTilingMode; + } + m_quickTileCombineTimer->stop(); + } + active_client->setQuickTileMode(mode, true); } diff --git a/workspace.cpp b/workspace.cpp index 7b4c6a84d6..bf9b100beb 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -129,6 +129,8 @@ Workspace::Workspace() , set_active_client_recursion(0) , block_stacking_updates(0) , m_sessionManager(new SessionManager(this)) + , m_quickTileCombineTimer(nullptr) + , m_lastTilingMode(0) { // If KWin was already running it saved its configuration after loosing the selection -> Reread QFuture reparseConfigFuture = QtConcurrent::run(options, &Options::reparseConfiguration); @@ -155,6 +157,9 @@ Workspace::Workspace() delayFocusTimer = nullptr; + m_quickTileCombineTimer = new QTimer(this); + m_quickTileCombineTimer->setSingleShot(true); + RuleBook::create(this)->load(); kwinApp()->createScreens(); diff --git a/workspace.h b/workspace.h index 831084e088..e6884d6b92 100644 --- a/workspace.h +++ b/workspace.h @@ -250,6 +250,8 @@ public: private: Compositor *m_compositor; + QTimer *m_quickTileCombineTimer; + QuickTileMode m_lastTilingMode; //------------------------------------------------- // Unsorted