Add support for keyboard layout switching policy "winclass"
Summary: This is quite similar to the policy "window" - the main difference is that windows from the same application share the layout. So only switching to a window from another application changes the layout. This change is the last policy to add for support of all the policies we have on X11. Reviewers: #kwin, #plasma Subscribers: plasma-devel Tags: #plasma Differential Revision: https://phabricator.kde.org/D5365
This commit is contained in:
parent
c8274dbe57
commit
b132fe7c24
3 changed files with 155 additions and 0 deletions
|
@ -59,6 +59,7 @@ private Q_SLOTS:
|
||||||
void testDBusServiceExport();
|
void testDBusServiceExport();
|
||||||
void testVirtualDesktopPolicy();
|
void testVirtualDesktopPolicy();
|
||||||
void testWindowPolicy();
|
void testWindowPolicy();
|
||||||
|
void testApplicationPolicy();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void reconfigureLayouts();
|
void reconfigureLayouts();
|
||||||
|
@ -394,5 +395,67 @@ void KeyboardLayoutTest::testWindowPolicy()
|
||||||
QTRY_COMPARE(xkb->layoutName(), QStringLiteral("German (Neo 2)"));
|
QTRY_COMPARE(xkb->layoutName(), QStringLiteral("German (Neo 2)"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void KeyboardLayoutTest::testApplicationPolicy()
|
||||||
|
{
|
||||||
|
KConfigGroup layoutGroup = kwinApp()->kxkbConfig()->group("Layout");
|
||||||
|
layoutGroup.writeEntry("LayoutList", QStringLiteral("us,de,de(neo)"));
|
||||||
|
layoutGroup.writeEntry("SwitchMode", QStringLiteral("WinClass"));
|
||||||
|
layoutGroup.sync();
|
||||||
|
reconfigureLayouts();
|
||||||
|
auto xkb = input()->keyboard()->xkb();
|
||||||
|
QTRY_COMPARE(xkb->numberOfLayouts(), 3u);
|
||||||
|
QTRY_COMPARE(xkb->layoutName(), QStringLiteral("English (US)"));
|
||||||
|
|
||||||
|
// create a window
|
||||||
|
using namespace KWayland::Client;
|
||||||
|
QScopedPointer<Surface> surface(Test::createSurface());
|
||||||
|
QScopedPointer<ShellSurface> shellSurface(Test::createShellSurface(surface.data()));
|
||||||
|
auto c1 = Test::renderAndWaitForShown(surface.data(), QSize(100, 100), Qt::blue);
|
||||||
|
QVERIFY(c1);
|
||||||
|
|
||||||
|
// now switch layout
|
||||||
|
auto changeLayout = [] (const QString &layoutName) {
|
||||||
|
QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.keyboard"), QStringLiteral("/Layouts"), QStringLiteral("org.kde.KeyboardLayouts"), QStringLiteral("setLayout"));
|
||||||
|
msg << layoutName;
|
||||||
|
return QDBusConnection::sessionBus().asyncCall(msg);
|
||||||
|
};
|
||||||
|
LayoutChangedSignalWrapper wrapper;
|
||||||
|
QSignalSpy layoutChangedSpy(&wrapper, &LayoutChangedSignalWrapper::layoutChanged);
|
||||||
|
QVERIFY(layoutChangedSpy.isValid());
|
||||||
|
auto reply = changeLayout(QStringLiteral("German"));
|
||||||
|
QVERIFY(layoutChangedSpy.wait());
|
||||||
|
QCOMPARE(layoutChangedSpy.count(), 1);
|
||||||
|
reply.waitForFinished();
|
||||||
|
QTRY_COMPARE(xkb->layoutName(), QStringLiteral("German"));
|
||||||
|
|
||||||
|
// create a second window
|
||||||
|
QScopedPointer<Surface> surface2(Test::createSurface());
|
||||||
|
QScopedPointer<ShellSurface> shellSurface2(Test::createShellSurface(surface2.data()));
|
||||||
|
auto c2 = Test::renderAndWaitForShown(surface2.data(), QSize(100, 100), Qt::red);
|
||||||
|
QVERIFY(c2);
|
||||||
|
// it is the same application and should not switch the layout
|
||||||
|
QVERIFY(!layoutChangedSpy.wait());
|
||||||
|
QCOMPARE(layoutChangedSpy.count(), 1);
|
||||||
|
// now change to another layout
|
||||||
|
reply = changeLayout(QStringLiteral("German (Neo 2)"));
|
||||||
|
QVERIFY(layoutChangedSpy.wait());
|
||||||
|
QCOMPARE(layoutChangedSpy.count(), 2);
|
||||||
|
QTRY_COMPARE(xkb->layoutName(), QStringLiteral("German (Neo 2)"));
|
||||||
|
|
||||||
|
// activate other window
|
||||||
|
workspace()->activateClient(c1);
|
||||||
|
QVERIFY(!layoutChangedSpy.wait());
|
||||||
|
QTRY_COMPARE(xkb->layoutName(), QStringLiteral("German (Neo 2)"));
|
||||||
|
workspace()->activateClient(c2);
|
||||||
|
QVERIFY(!layoutChangedSpy.wait());
|
||||||
|
QTRY_COMPARE(xkb->layoutName(), QStringLiteral("German (Neo 2)"));
|
||||||
|
|
||||||
|
shellSurface2.reset();
|
||||||
|
surface2.reset();
|
||||||
|
QVERIFY(Test::waitForWindowDestroyed(c2));
|
||||||
|
QVERIFY(!layoutChangedSpy.wait());
|
||||||
|
QTRY_COMPARE(xkb->layoutName(), QStringLiteral("German (Neo 2)"));
|
||||||
|
}
|
||||||
|
|
||||||
WAYLANDTEST_MAIN(KeyboardLayoutTest)
|
WAYLANDTEST_MAIN(KeyboardLayoutTest)
|
||||||
#include "keyboard_layout_test.moc"
|
#include "keyboard_layout_test.moc"
|
||||||
|
|
|
@ -60,6 +60,9 @@ Policy *Policy::create(Xkb *xkb, KeyboardLayout *layout, const QString &policy)
|
||||||
if (policy.toLower() == QStringLiteral("window")) {
|
if (policy.toLower() == QStringLiteral("window")) {
|
||||||
return new WindowPolicy(xkb, layout);
|
return new WindowPolicy(xkb, layout);
|
||||||
}
|
}
|
||||||
|
if (policy.toLower() == QStringLiteral("winclass")) {
|
||||||
|
return new ApplicationPolicy(xkb, layout);
|
||||||
|
}
|
||||||
return new GlobalPolicy(xkb, layout);
|
return new GlobalPolicy(xkb, layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,5 +185,74 @@ void WindowPolicy::layoutChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ApplicationPolicy::ApplicationPolicy(KWin::Xkb* xkb, KWin::KeyboardLayout* layout)
|
||||||
|
: Policy(xkb, layout)
|
||||||
|
{
|
||||||
|
connect(workspace(), &Workspace::clientActivated, this, &ApplicationPolicy::clientActivated);
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplicationPolicy::~ApplicationPolicy()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplicationPolicy::clientActivated(AbstractClient *c)
|
||||||
|
{
|
||||||
|
if (!c) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// ignore some special types
|
||||||
|
if (c->isDesktop() || c->isDock()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
quint32 layout = 0;
|
||||||
|
for (auto it = m_layouts.constBegin(); it != m_layouts.constEnd(); it++) {
|
||||||
|
if (AbstractClient::belongToSameApplication(c, it.key())) {
|
||||||
|
layout = it.value();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setLayout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplicationPolicy::clearCache()
|
||||||
|
{
|
||||||
|
m_layouts.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplicationPolicy::layoutChanged()
|
||||||
|
{
|
||||||
|
auto c = workspace()->activeClient();
|
||||||
|
if (!c) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// ignore some special types
|
||||||
|
if (c->isDesktop() || c->isDock()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = m_layouts.find(c);
|
||||||
|
const auto l = layout();
|
||||||
|
if (it == m_layouts.constEnd()) {
|
||||||
|
m_layouts.insert(c, l);
|
||||||
|
connect(c, &AbstractClient::windowClosed, this,
|
||||||
|
[this, c] {
|
||||||
|
m_layouts.remove(c);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if (it.value() == l) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
it.value() = l;
|
||||||
|
}
|
||||||
|
// update all layouts for the application
|
||||||
|
for (it = m_layouts.begin(); it != m_layouts.end(); it++) {
|
||||||
|
if (!AbstractClient::belongToSameApplication(it.key(), c)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
it.value() = l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,6 +112,26 @@ private:
|
||||||
QHash<AbstractClient*, quint32> m_layouts;
|
QHash<AbstractClient*, quint32> m_layouts;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ApplicationPolicy : public Policy
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit ApplicationPolicy(Xkb *xkb, KeyboardLayout *layout);
|
||||||
|
~ApplicationPolicy() override;
|
||||||
|
|
||||||
|
QString name() const override {
|
||||||
|
return QStringLiteral("WinClass");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void clearCache() override;
|
||||||
|
void layoutChanged() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void clientActivated(AbstractClient *c);
|
||||||
|
QHash<AbstractClient*, quint32> m_layouts;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue