mirror of
https://invent.kde.org/plasma/plasma-desktop.git
synced 2026-11-03 14:49:44 -09:00
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
This commit is contained in:
parent
cab933b069
commit
6d3514d331
3 changed files with 228 additions and 15 deletions
|
|
@ -12,7 +12,6 @@ import Qt5Compat.GraphicalEffects
|
|||
|
||||
import org.kde.plasma.components as PlasmaComponents3
|
||||
import org.kde.plasma.private.keyboardindicator as KeyboardIndicator
|
||||
import org.kde.plasma.workspace.keyboardlayout as Keyboards
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.breeze.components
|
||||
|
|
@ -65,7 +64,7 @@ Item {
|
|||
anchors.fill: parent
|
||||
|
||||
property bool uiVisible: true
|
||||
property bool blockUI: mainStack.depth > 1 || userListComponent.mainPasswordBox.text.length > 0 || Keyboards.KWinVirtualKeyboard.visible || config.type !== "image"
|
||||
property bool blockUI: mainStack.depth > 1 || userListComponent.mainPasswordBox.text.length > 0 || inputPanel.keyboardVisible || config.type !== "image"
|
||||
|
||||
hoverEnabled: true
|
||||
drag.filterChildren: true
|
||||
|
|
@ -207,7 +206,7 @@ Item {
|
|||
return parts.join(" • ");
|
||||
}
|
||||
|
||||
actionItemsVisible: !Keyboards.KWinVirtualKeyboard.visible
|
||||
actionItemsVisible: !inputPanel.keyboardVisible
|
||||
actionItems: [
|
||||
ActionButton {
|
||||
icon.name: "system-hibernate"
|
||||
|
|
@ -315,6 +314,17 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
VirtualKeyboard {
|
||||
id: inputPanel
|
||||
|
||||
z: 1
|
||||
|
||||
screenRoot: root
|
||||
mainStack: mainStack
|
||||
mainBlock: userListComponent
|
||||
passwordField: userListComponent.mainPasswordBox
|
||||
}
|
||||
|
||||
Component {
|
||||
id: userPromptComponent
|
||||
Login {
|
||||
|
|
@ -341,7 +351,7 @@ Item {
|
|||
sddm.login(username, password, sessionButton.currentIndex)
|
||||
}
|
||||
|
||||
actionItemsVisible: !Keyboards.KWinVirtualKeyboard.visible
|
||||
actionItemsVisible: !inputPanel.keyboardVisible
|
||||
actionItems: [
|
||||
ActionButton {
|
||||
icon.name: "system-suspend"
|
||||
|
|
@ -438,17 +448,9 @@ Item {
|
|||
id: virtualKeyboardButton
|
||||
|
||||
text: i18ndc("plasma-desktop-sddm-theme", "Button to show/hide virtual keyboard", "Virtual Keyboard")
|
||||
icon.name: Keyboards.KWinVirtualKeyboard.visible ? "input-keyboard-virtual-on" : "input-keyboard-virtual-off"
|
||||
onClicked: {
|
||||
if (Keyboards.KWinVirtualKeyboard.visible) {
|
||||
Keyboards.KWinVirtualKeyboard.active = false;
|
||||
} else {
|
||||
// Otherwise the password field loses focus and on-screen keyboard
|
||||
// keystrokes get eaten
|
||||
userListComponent.mainPasswordBox.forceActiveFocus();
|
||||
Keyboards.KWinVirtualKeyboard.forceActivate();
|
||||
}
|
||||
}
|
||||
icon.name: inputPanel.keyboardVisible ? "input-keyboard-virtual-on" : "input-keyboard-virtual-off"
|
||||
onClicked: inputPanel.toggle()
|
||||
visible: inputPanel.available
|
||||
|
||||
Layout.fillHeight: true
|
||||
containmentMask: Item {
|
||||
|
|
|
|||
28
sddm-theme/QtVirtualKeyboard.qml
Normal file
28
sddm-theme/QtVirtualKeyboard.qml
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2017 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.VirtualKeyboard as QtVK
|
||||
|
||||
/*
|
||||
* Qt Virtual Keyboard's input panel. Instantiating it is what keeps Qt from
|
||||
* creating its own DesktopInputPanel, which pops up whenever a text field
|
||||
* gains focus, mouse clicks included.
|
||||
*
|
||||
* The panel has no animation of its own: VirtualKeyboard.qml slides and fades
|
||||
* the loader instead. That way the keyboard also animates out when Qt hides
|
||||
* the input method on its own, which its built-in hide key does.
|
||||
*/
|
||||
QtVK.InputPanel {
|
||||
id: inputPanel
|
||||
property bool activated: false
|
||||
active: activated && InputMethod.visible
|
||||
// Stay visible while the loader slides the panel off-screen. Hiding it as
|
||||
// soon as InputMethod.visible turns false, which Qt Virtual Keyboard's own
|
||||
// hide key does, makes the keyboard disappear in a single frame.
|
||||
visible: activated
|
||||
width: parent.width
|
||||
}
|
||||
183
sddm-theme/VirtualKeyboard.qml
Normal file
183
sddm-theme/VirtualKeyboard.qml
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Reference in a new issue