Add support for keyboard layout switching policy "window"

Summary:
This policy stores the layout for each window which becomes active
and restores the layout once it gets activated again.

Test Plan: Added test case

Reviewers: #kwin, #plasma

Subscribers: plasma-devel

Tags: #plasma

Differential Revision: https://phabricator.kde.org/D5315
This commit is contained in:
Martin Gräßlin 2017-04-06 07:13:44 +02:00
parent bf99d9ffdd
commit c8274dbe57
3 changed files with 151 additions and 7 deletions

View file

@ -21,12 +21,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "keyboard_input.h"
#include "keyboard_layout.h"
#include "platform.h"
#include "shell_client.h"
#include "virtualdesktops.h"
#include "wayland_server.h"
#include "workspace.h"
#include <KConfigGroup>
#include <KGlobalAccel>
#include <KWayland/Client/surface.h>
#include <KWayland/Client/shell.h>
#include <QAction>
#include <QDBusConnection>
#include <QDBusConnectionInterface>
@ -53,6 +58,7 @@ private Q_SLOTS:
void testPerLayoutShortcut();
void testDBusServiceExport();
void testVirtualDesktopPolicy();
void testWindowPolicy();
private:
void reconfigureLayouts();
@ -67,6 +73,8 @@ void KeyboardLayoutTest::reconfigureLayouts()
void KeyboardLayoutTest::initTestCase()
{
qRegisterMetaType<KWin::ShellClient*>();
qRegisterMetaType<KWin::AbstractClient*>();
QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated);
QVERIFY(workspaceCreatedSpy.isValid());
kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024));
@ -82,10 +90,12 @@ void KeyboardLayoutTest::initTestCase()
void KeyboardLayoutTest::init()
{
QVERIFY(Test::setupWaylandConnection());
}
void KeyboardLayoutTest::cleanup()
{
Test::destroyWaylandConnection();
}
class LayoutChangedSignalWrapper : public QObject
@ -337,5 +347,52 @@ void KeyboardLayoutTest::testVirtualDesktopPolicy()
}
void KeyboardLayoutTest::testWindowPolicy()
{
KConfigGroup layoutGroup = kwinApp()->kxkbConfig()->group("Layout");
layoutGroup.writeEntry("LayoutList", QStringLiteral("us,de,de(neo)"));
layoutGroup.writeEntry("SwitchMode", QStringLiteral("Window"));
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);
};
auto reply = changeLayout(QStringLiteral("German"));
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);
// this should have switched back to English
QTRY_COMPARE(xkb->layoutName(), QStringLiteral("English (US)"));
// now change to another layout
reply = changeLayout(QStringLiteral("German (Neo 2)"));
reply.waitForFinished();
QTRY_COMPARE(xkb->layoutName(), QStringLiteral("German (Neo 2)"));
// activate other window
workspace()->activateClient(c1);
QTRY_COMPARE(xkb->layoutName(), QStringLiteral("German"));
workspace()->activateClient(c2);
QTRY_COMPARE(xkb->layoutName(), QStringLiteral("German (Neo 2)"));
}
WAYLANDTEST_MAIN(KeyboardLayoutTest)
#include "keyboard_layout_test.moc"

View file

@ -19,7 +19,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "keyboard_layout_switching.h"
#include "keyboard_layout.h"
#include "abstract_client.h"
#include "deleted.h"
#include "virtualdesktops.h"
#include "workspace.h"
#include "xkb.h"
namespace KWin
@ -54,6 +57,9 @@ Policy *Policy::create(Xkb *xkb, KeyboardLayout *layout, const QString &policy)
if (policy.toLower() == QStringLiteral("desktop")) {
return new VirtualDesktopPolicy(xkb, layout);
}
if (policy.toLower() == QStringLiteral("window")) {
return new WindowPolicy(xkb, layout);
}
return new GlobalPolicy(xkb, layout);
}
@ -77,19 +83,26 @@ void VirtualDesktopPolicy::clearCache()
m_layouts.clear();
}
namespace {
template <typename T, typename U>
quint32 getLayout(const T &layouts, const U &reference)
{
auto it = layouts.constFind(reference);
if (it == layouts.constEnd()) {
return 0;
} else {
return it.value();
}
}
}
void VirtualDesktopPolicy::desktopChanged()
{
auto d = VirtualDesktopManager::self()->currentDesktop();
if (!d) {
return;
}
auto it = m_layouts.constFind(d);
if (it == m_layouts.constEnd()) {
// new desktop - go to default;
setLayout(0);
} else {
setLayout(it.value());
}
setLayout(getLayout(m_layouts, d));
}
void VirtualDesktopPolicy::layoutChanged()
@ -115,5 +128,59 @@ void VirtualDesktopPolicy::layoutChanged()
}
}
WindowPolicy::WindowPolicy(KWin::Xkb* xkb, KWin::KeyboardLayout* layout)
: Policy(xkb, layout)
{
connect(workspace(), &Workspace::clientActivated, this,
[this] (AbstractClient *c) {
if (!c) {
return;
}
// ignore some special types
if (c->isDesktop() || c->isDock()) {
return;
}
setLayout(getLayout(m_layouts, c));
}
);
}
WindowPolicy::~WindowPolicy()
{
}
void WindowPolicy::clearCache()
{
m_layouts.clear();
}
void WindowPolicy::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;
}
}
}
}

View file

@ -26,6 +26,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
namespace KWin
{
class AbstractClient;
class KeyboardLayout;
class Xkb;
class VirtualDesktop;
@ -92,6 +93,25 @@ private:
QHash<VirtualDesktop *, quint32> m_layouts;
};
class WindowPolicy : public Policy
{
Q_OBJECT
public:
explicit WindowPolicy(Xkb *xkb, KeyboardLayout *layout);
~WindowPolicy() override;
QString name() const override {
return QStringLiteral("Window");
}
protected:
void clearCache() override;
void layoutChanged() override;
private:
QHash<AbstractClient*, quint32> m_layouts;
};
}
}