Allow corner-tiling by quickly combining edge tiling shortcuts
Currently the only way for a uuser to invoke corner-tiling is to manually set shortcuts for them. This patch adds another option: invoke the existing shortcuts for edge tiling in a combined manner in quick succession. For example, hitting Meta+Left and then Meta+Up within a one-second period will tile the active window into the top left corner. In practice you hold down the Meta key and then press Left then Up (or Up and then Left), and I think it feels very natural. Linux Mint's window manager has this feature and I always missed it when I left Mint for the KDE world. Autotests for existing tiling shortcuts are adjusted to not break, and additional tests for the new tiling options are added.
This commit is contained in:
parent
e9c68f36bd
commit
87578bfc15
4 changed files with 60 additions and 24 deletions
|
@ -709,21 +709,25 @@ void QuickTilingTest::testX11QuickTilingAfterVertMaximize()
|
||||||
|
|
||||||
void QuickTilingTest::testShortcut_data()
|
void QuickTilingTest::testShortcut_data()
|
||||||
{
|
{
|
||||||
QTest::addColumn<QString>("shortcut");
|
QTest::addColumn<QStringList>("shortcutList");
|
||||||
QTest::addColumn<QuickTileMode>("expectedMode");
|
QTest::addColumn<QuickTileMode>("expectedMode");
|
||||||
QTest::addColumn<QRect>("expectedGeometry");
|
QTest::addColumn<QRect>("expectedGeometry");
|
||||||
|
|
||||||
#define FLAG(name) QuickTileMode(QuickTileFlag::name)
|
#define FLAG(name) QuickTileMode(QuickTileFlag::name)
|
||||||
QTest::newRow("top") << QStringLiteral("Window Quick Tile Top") << FLAG(Top) << QRect(0, 0, 1280, 512);
|
QTest::newRow("top") << QStringList{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") << QStringList{QStringLiteral("Window Quick Tile Bottom")} << FLAG(Bottom) << QRect(0, 512, 1280, 512);
|
||||||
QTest::newRow("bottom") << 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("right") << QStringLiteral("Window Quick Tile Right") << FLAG(Right) << QRect(640, 0, 640, 1024);
|
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("top right") << QStringLiteral("Window Quick Tile Top Right") << (FLAG(Top) | FLAG(Right)) << QRect(640, 0, 640, 512);
|
QTest::newRow("bottom left") << QStringList{QStringLiteral("Window Quick Tile Bottom Left")} << (FLAG(Bottom) | FLAG(Left)) << QRect(0, 512, 640, 512);
|
||||||
QTest::newRow("top left") << QStringLiteral("Window Quick Tile Top Left") << (FLAG(Top) | FLAG(Left)) << QRect(0, 0, 640, 512);
|
QTest::newRow("left") << QStringList{QStringLiteral("Window Quick Tile Left")} << FLAG(Left) << QRect(0, 0, 640, 1024);
|
||||||
QTest::newRow("bottom right") << QStringLiteral("Window Quick Tile Bottom Right") << (FLAG(Bottom) | FLAG(Right)) << QRect(640, 512, 640, 512);
|
QTest::newRow("right") << QStringList{QStringLiteral("Window Quick Tile Right")} << FLAG(Right) << QRect(640, 0, 640, 1024);
|
||||||
QTest::newRow("bottom left") << QStringLiteral("Window Quick Tile Bottom Left") << (FLAG(Bottom) | FLAG(Left)) << QRect(0, 512, 640, 512);
|
|
||||||
|
|
||||||
|
// 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
|
#undef FLAG
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -749,22 +753,30 @@ void QuickTilingTest::testShortcut()
|
||||||
QVERIFY(configureRequestedSpy.wait());
|
QVERIFY(configureRequestedSpy.wait());
|
||||||
QCOMPARE(configureRequestedSpy.count(), 1);
|
QCOMPARE(configureRequestedSpy.count(), 1);
|
||||||
|
|
||||||
QFETCH(QString, shortcut);
|
QFETCH(QStringList, shortcutList);
|
||||||
QFETCH(QRect, expectedGeometry);
|
QFETCH(QRect, expectedGeometry);
|
||||||
|
|
||||||
// invoke global shortcut through dbus
|
const int numberOfQuickTileActions = shortcutList.count();
|
||||||
auto msg = QDBusMessage::createMethodCall(
|
|
||||||
QStringLiteral("org.kde.kglobalaccel"),
|
if (numberOfQuickTileActions > 1) {
|
||||||
QStringLiteral("/component/kwin"),
|
QTest::qWait(1001);
|
||||||
QStringLiteral("org.kde.kglobalaccel.Component"),
|
}
|
||||||
QStringLiteral("invokeShortcut"));
|
|
||||||
msg.setArguments(QList<QVariant>{shortcut});
|
for (QString shortcut : shortcutList) {
|
||||||
QDBusConnection::sessionBus().asyncCall(msg);
|
// 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<QVariant>{shortcut});
|
||||||
|
QDBusConnection::sessionBus().asyncCall(msg);
|
||||||
|
}
|
||||||
|
|
||||||
QSignalSpy quickTileChangedSpy(c, &AbstractClient::quickTileModeChanged);
|
QSignalSpy quickTileChangedSpy(c, &AbstractClient::quickTileModeChanged);
|
||||||
QVERIFY(quickTileChangedSpy.isValid());
|
QVERIFY(quickTileChangedSpy.isValid());
|
||||||
QVERIFY(quickTileChangedSpy.wait());
|
QVERIFY(quickTileChangedSpy.wait());
|
||||||
QCOMPARE(quickTileChangedSpy.count(), 1);
|
QCOMPARE(quickTileChangedSpy.count(), numberOfQuickTileActions);
|
||||||
// at this point the geometry did not yet change
|
// at this point the geometry did not yet change
|
||||||
QCOMPARE(c->frameGeometry(), QRect(0, 0, 100, 50));
|
QCOMPARE(c->frameGeometry(), QRect(0, 0, 100, 50));
|
||||||
// but quick tile mode already changed
|
// but quick tile mode already changed
|
||||||
|
@ -795,15 +807,13 @@ void QuickTilingTest::testScript_data()
|
||||||
|
|
||||||
#define FLAG(name) QuickTileMode(QuickTileFlag::name)
|
#define FLAG(name) QuickTileMode(QuickTileFlag::name)
|
||||||
QTest::newRow("top") << QStringLiteral("Top") << FLAG(Top) << QRect(0, 0, 1280, 512);
|
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("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 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("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 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("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
|
#undef FLAG
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#include <QRect>
|
#include <QRect>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
namespace KWin
|
namespace KWin
|
||||||
{
|
{
|
||||||
|
@ -837,6 +838,24 @@ void Workspace::quickTileWindow(QuickTileMode mode)
|
||||||
return;
|
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);
|
active_client->setQuickTileMode(mode, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -129,6 +129,8 @@ Workspace::Workspace()
|
||||||
, set_active_client_recursion(0)
|
, set_active_client_recursion(0)
|
||||||
, block_stacking_updates(0)
|
, block_stacking_updates(0)
|
||||||
, m_sessionManager(new SessionManager(this))
|
, 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
|
// If KWin was already running it saved its configuration after loosing the selection -> Reread
|
||||||
QFuture<void> reparseConfigFuture = QtConcurrent::run(options, &Options::reparseConfiguration);
|
QFuture<void> reparseConfigFuture = QtConcurrent::run(options, &Options::reparseConfiguration);
|
||||||
|
@ -155,6 +157,9 @@ Workspace::Workspace()
|
||||||
|
|
||||||
delayFocusTimer = nullptr;
|
delayFocusTimer = nullptr;
|
||||||
|
|
||||||
|
m_quickTileCombineTimer = new QTimer(this);
|
||||||
|
m_quickTileCombineTimer->setSingleShot(true);
|
||||||
|
|
||||||
RuleBook::create(this)->load();
|
RuleBook::create(this)->load();
|
||||||
|
|
||||||
kwinApp()->createScreens();
|
kwinApp()->createScreens();
|
||||||
|
|
|
@ -250,6 +250,8 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Compositor *m_compositor;
|
Compositor *m_compositor;
|
||||||
|
QTimer *m_quickTileCombineTimer;
|
||||||
|
QuickTileMode m_lastTilingMode;
|
||||||
|
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
// Unsorted
|
// Unsorted
|
||||||
|
|
Loading…
Reference in a new issue