mirror-kde-plasma-desktop/sddm-theme/VirtualKeyboard.qml
Fushan Wen 6d3514d331 sddm-theme: restore Qt Virtual Keyboard fallback on X11
SDDM 0.21 still runs its greeter on X11 and sets
QT_IM_MODULE=qtvirtualkeyboard, but since b1eed7f7 the theme only forwards
to Keyboards.KWinVirtualKeyboard, which only exists under a running KWin.
Qt Virtual Keyboard stays loaded as the input method, and because the theme
no longer instantiates its input panel, Qt creates its own
DesktopInputPanel, which pops up whenever a text field gains focus, a plain
mouse click included. The keyboard button was dead there as well, because
KWinVirtualKeyboard.available is false and its visible property never
becomes true.

Bring the X11 fallback back as a theme-private component: on X11
VirtualKeyboard.qml loads the Qt Virtual Keyboard panel that
plasma-workspace dropped in f1c83d15 (instantiating QtVK.InputPanel is what
keeps Qt from creating its own panel), on Wayland it keeps forwarding to
Keyboards.KWinVirtualKeyboard. The button is hidden when no virtual
keyboard is available at all.

The panel itself carries no animation: it is shown and hidden by animating
the loader that holds it, which slides it below the bottom edge while
fading it out. Keeping the animation in one place matters because Qt
Virtual Keyboard's own hide key turns InputMethod.visible off before the
theme is notified, and a panel that hid itself as soon as it became
inactive would disappear in a single frame instead of sliding out. The
panel is only deactivated once it is fully off-screen, so that hiding it
does not cut the animation short.

BUG: 527138
2026-11-03 02:51:22 +08:00

183 lines
6.4 KiB
QML

/*
SPDX-FileCopyrightText: 2016 David Edmundson <davidedmundson@kde.org>
SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
SPDX-FileCopyrightText: 2023 ivan tkachenko <me@ratijas.tk>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
import QtQuick
import QtQuick.Templates as T
import org.kde.kirigami as Kirigami
import org.kde.plasma.workspace.keyboardlayout as Keyboards
/*
* Shows and hides the virtual keyboard and keeps the login form above it.
*
* On Wayland the keyboard is drawn by KWin, so all we do is forward to
* Keyboards.KWinVirtualKeyboard. On X11, which is still what SDDM uses by
* default, there is no KWin to talk to, so the greeter falls back to Qt
* Virtual Keyboard instead.
*
* Instantiating Qt Virtual Keyboard's input panel is also what keeps Qt from
* creating its own DesktopInputPanel, which pops up whenever a text field
* gains focus, mouse clicks included. See
* https://bugs.kde.org/show_bug.cgi?id=527138
*/
Loader {
id: loader
// Usually, anchors on a top-level component is a bad idea, but this is a
// tightly integrated component sharing the screen with the theme.
anchors {
left: parent.left
right: parent.right
}
required property Item screenRoot
required property T.StackView mainStack
required property /* MainBlock | Login */ Item mainBlock
required property Item passwordField
// Whether the keyboard is currently on screen.
readonly property bool keyboardVisible: active ? state === "visible" : Keyboards.KWinVirtualKeyboard.visible
// Whether this platform has a virtual keyboard at all.
readonly property bool available: active ? status === Loader.Ready : Keyboards.KWinVirtualKeyboard.available
readonly property bool keyboardActive: (item as QtVirtualKeyboard)?.active ?? false
active: !Qt.platform.pluginName.includes("wayland")
source: Qt.resolvedUrl("QtVirtualKeyboard.qml")
function toggle() {
if (!active) {
if (Keyboards.KWinVirtualKeyboard.visible) {
Keyboards.KWinVirtualKeyboard.active = false;
} else {
// Otherwise the password field loses focus and on-screen
// keyboard keystrokes get eaten
passwordField.forceActiveFocus();
Keyboards.KWinVirtualKeyboard.forceActivate();
}
return;
}
if (!item) {
return;
}
if (state === "hidden") {
// Otherwise the password field loses focus and on-screen keyboard
// keystrokes get eaten
passwordField.forceActiveFocus();
state = "visible";
} else {
state = "hidden";
}
}
onKeyboardActiveChanged: {
// The keyboard was hidden by something other than the button, e.g. Qt
// Virtual Keyboard's own hide key, which turns InputMethod.visible off
// before the theme is notified. Animate it out.
if (!keyboardActive && state === "visible") {
state = "hidden";
}
}
state: "hidden"
states: [
State {
name: "visible"
PropertyChanges {
mainStack.y: Math.min(0, screenRoot.height - loader.height - mainBlock.visibleBoundary)
}
PropertyChanges {
loader.y: screenRoot.height - loader.height
loader.opacity: 1
}
},
State {
name: "hidden"
PropertyChanges {
mainStack.y: 0
}
PropertyChanges {
loader.y: screenRoot.height
loader.opacity: 0
}
}
]
transitions: [
Transition {
from: "hidden"
to: "visible"
SequentialAnimation {
ScriptAction {
script: {
loader.item.activated = true;
Qt.inputMethod.show();
}
}
// The loader slides the keyboard in from below the screen
// while fading it in, and moves the form out of the way
ParallelAnimation {
NumberAnimation {
target: loader.mainStack
property: "y"
duration: Kirigami.Units.longDuration
easing.type: Easing.InOutQuad
}
NumberAnimation {
target: loader
property: "y"
duration: Kirigami.Units.longDuration
easing.type: Easing.OutQuad
}
NumberAnimation {
target: loader
property: "opacity"
duration: Kirigami.Units.longDuration
easing.type: Easing.OutQuad
}
}
}
},
Transition {
from: "visible"
to: "hidden"
SequentialAnimation {
ParallelAnimation {
NumberAnimation {
target: loader.mainStack
property: "y"
duration: Kirigami.Units.longDuration
easing.type: Easing.InOutQuad
}
// Nothing animates the keyboard out of the way, so slide
// it off-screen and fade it out ourselves
NumberAnimation {
target: loader
property: "y"
duration: Kirigami.Units.longDuration
easing.type: Easing.InQuad
}
NumberAnimation {
target: loader
property: "opacity"
duration: Kirigami.Units.longDuration
easing.type: Easing.InQuad
}
}
// Deactivate only once the keyboard has slid out, otherwise it
// vanishes before the animation starts
ScriptAction {
script: {
loader.item.activated = false;
Qt.inputMethod.hide();
}
}
}
}
]
}