Follow Plasma's virtual keyboard setting

KWin activates the input method for every text field; its policy (the
tray toggle) only gates the input panel, which sloposk doesn't use. sloposk
now asks KWin over D-Bus whether a focused field should show the
keyboard: never when off, only after touch or a pen by default.
"Always Show" and D-Bus Show/Toggle still show it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-25 17:31:13 +01:00
co-authored by Claude Opus 5.5
parent 499e5ee76e
commit b745e9da1b
8 changed files with 105 additions and 4 deletions
+1
View File
@@ -81,6 +81,7 @@ qt_add_executable(sloposk
app/DevHooks.h app/DevHooks.cpp
app/DBusControl.h app/DBusControl.cpp
app/Checks.h app/Checks.cpp
app/KWinVirtualKeyboard.h app/KWinVirtualKeyboard.cpp
app/QuitOnSignal.h app/QuitOnSignal.cpp
app/SystemLayouts.h app/SystemLayouts.cpp
backend/InputMethodProtocol.h
+30
View File
@@ -0,0 +1,30 @@
#include "KWinVirtualKeyboard.h"
#include <QDBusConnection>
#include <QDBusMessage>
#include <QDBusPendingCallWatcher>
#include <QDBusPendingReply>
namespace {
const QString Service = QStringLiteral("org.kde.KWin");
const QString Path = QStringLiteral("/VirtualKeyboard");
const QString Interface = QStringLiteral("org.kde.kwin.VirtualKeyboard");
} // namespace
KWinVirtualKeyboard::KWinVirtualKeyboard(QObject *parent)
: QObject(parent)
{
QDBusConnection::sessionBus().connect(Service, Path, Interface, QStringLiteral("modeChanged"), this,
SIGNAL(modeChanged()));
}
void KWinVirtualKeyboard::askShowOnActive(QObject *context, std::function<void(bool)> answer)
{
const QDBusMessage call = QDBusMessage::createMethodCall(Service, Path, Interface, QStringLiteral("willShowOnActive"));
auto *watcher = new QDBusPendingCallWatcher(QDBusConnection::sessionBus().asyncCall(call), context);
connect(watcher, &QDBusPendingCallWatcher::finished, context, [answer = std::move(answer)](QDBusPendingCallWatcher *watcher) {
watcher->deleteLater();
const QDBusPendingReply<bool> reply = *watcher;
answer(reply.isError() || reply.value());
});
}
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include <QObject>
#include <functional>
// KWin's virtual keyboard policy (the tray toggle, System Settings →
// Virtual Keyboard): KWin activates the input method for every text field
// and the policy only gates its input panel, which sloposk doesn't use, so
// sloposk asks it over org.kde.KWin /VirtualKeyboard instead.
class KWinVirtualKeyboard : public QObject
{
Q_OBJECT
public:
explicit KWinVirtualKeyboard(QObject *parent = nullptr);
// Calls `answer` with whether a field focused now should show the
// keyboard: never when it's off, only after touch or a pen in the
// default mode. True if KWin can't be asked.
void askShowOnActive(QObject *context, std::function<void(bool)> answer);
Q_SIGNALS:
void modeChanged();
};
+24 -2
View File
@@ -1,6 +1,7 @@
#include "app/Checks.h"
#include "app/DBusControl.h"
#include "app/DevHooks.h"
#include "app/KWinVirtualKeyboard.h"
#include "app/QuitOnSignal.h"
#include "app/SystemLayouts.h"
#include "backend/KWinInputMethod.h"
@@ -89,6 +90,27 @@ void restoreAndPersistGeometry(FloatingWindow &placement, WindowSettings &settin
[&] { settings.save(placement.screenName(), placement.geometry()); });
}
// A focused field shows the keyboard only if KWin's policy wants one now;
// asked before showing, so a "no" doesn't flash the keyboard.
void connectFieldFocus(VisibilityController &visibility, KWinInputMethod &backend, KWinVirtualKeyboard &policy)
{
QObject::connect(&backend, &TextBackend::activeChanged, &visibility, [&] {
if (!backend.isActive()) {
visibility.setFieldActive(false);
return;
}
policy.askShowOnActive(&visibility, [&](bool show) {
if (!backend.isActive())
return; // left the field before KWin answered
visibility.setShowOnField(show);
visibility.setFieldActive(true);
});
});
QObject::connect(&policy, &KWinVirtualKeyboard::modeChanged, &visibility, [&] {
policy.askShowOnActive(&visibility, [&](bool show) { visibility.setShowOnField(show); });
});
}
void connectVisibility(VisibilityController &visibility, KWinInputMethod &backend, const Settings &settings,
KeyboardController &controller, FloatingWindow &placement)
{
@@ -99,8 +121,6 @@ void connectVisibility(VisibilityController &visibility, KWinInputMethod &backen
// work) there is no focus signal: stay visible so it can be tried out.
const auto updateStandalone = [&] { visibility.setStandalone(!backend.isAvailable()); };
QObject::connect(&backend, &KWinInputMethod::availableChanged, &visibility, updateStandalone);
QObject::connect(&backend, &TextBackend::activeChanged, &visibility,
[&] { visibility.setFieldActive(backend.isActive()); });
QObject::connect(&controller, &KeyboardController::dismissRequested, &visibility, &VisibilityController::hide);
QObject::connect(&visibility, &VisibilityController::visibleChanged, &placement,
[&](bool visible) { visible ? placement.show() : placement.hide(); });
@@ -175,6 +195,8 @@ int main(int argc, char *argv[])
connectKeySettings(settings, model, placement, *window, rebuildLayout);
VisibilityController visibility;
KWinVirtualKeyboard virtualKeyboardPolicy;
connectFieldFocus(visibility, backend, virtualKeyboardPolicy);
connectVisibility(visibility, backend, settings, controller, placement);
DBusControl dbus(visibility);
+7 -1
View File
@@ -7,6 +7,12 @@ void VisibilityController::setFieldActive(bool active)
update();
}
void VisibilityController::setShowOnField(bool show)
{
m_showOnField = show;
update();
}
void VisibilityController::setStandalone(bool standalone)
{
m_standalone = standalone;
@@ -40,7 +46,7 @@ void VisibilityController::toggle()
void VisibilityController::update()
{
const bool visible = m_override.value_or(m_alwaysVisible || m_standalone || m_fieldActive);
const bool visible = m_override.value_or(m_alwaysVisible || m_standalone || (m_fieldActive && m_showOnField));
if (visible == m_visible)
return;
m_visible = visible;
+4 -1
View File
@@ -4,7 +4,7 @@
#include <optional>
// Decides whether the keyboard is shown:
// - while a text field is focused
// - while a text field is focused, if the system wants a keyboard for it
// - always, if the user wants it (to type into any window) or when running
// standalone (no input-method connection)
// - show()/hide()/toggle() override that until the focus changes, also
@@ -20,6 +20,8 @@ public:
// Called on every focus change, even when a field follows a field.
void setFieldActive(bool active);
// KWin's policy (off, or touch only) for fields focused from now on.
void setShowOnField(bool show);
void setStandalone(bool standalone);
void setAlwaysVisible(bool always);
@@ -34,6 +36,7 @@ private:
void update();
bool m_fieldActive = false;
bool m_showOnField = true;
bool m_standalone = false;
bool m_alwaysVisible = false;
std::optional<bool> m_override;