Changeset View
Changeset View
Standalone View
Standalone View
virtualkeyboard.cpp
Show All 20 Lines | |||||
21 | #include "virtualkeyboard_dbus.h" | 21 | #include "virtualkeyboard_dbus.h" | ||
22 | #include "input.h" | 22 | #include "input.h" | ||
23 | #include "keyboard_input.h" | 23 | #include "keyboard_input.h" | ||
24 | #include "utils.h" | 24 | #include "utils.h" | ||
25 | #include "screens.h" | 25 | #include "screens.h" | ||
26 | #include "wayland_server.h" | 26 | #include "wayland_server.h" | ||
27 | #include "workspace.h" | 27 | #include "workspace.h" | ||
28 | #include "xkb.h" | 28 | #include "xkb.h" | ||
29 | #include "shell_client.h" | ||||
29 | 30 | | |||
30 | #include <KWayland/Server/display.h> | 31 | #include <KWayland/Server/display.h> | ||
31 | #include <KWayland/Server/seat_interface.h> | 32 | #include <KWayland/Server/seat_interface.h> | ||
32 | #include <KWayland/Server/textinput_interface.h> | 33 | #include <KWayland/Server/textinput_interface.h> | ||
34 | #include <KWayland/Server/surface_interface.h> | ||||
33 | 35 | | |||
34 | #include <KStatusNotifierItem> | 36 | #include <KStatusNotifierItem> | ||
35 | #include <KLocalizedString> | 37 | #include <KLocalizedString> | ||
36 | 38 | | |||
37 | #include <QDBusConnection> | 39 | #include <QDBusConnection> | ||
38 | #include <QDBusPendingCall> | 40 | #include <QDBusPendingCall> | ||
39 | #include <QDBusMessage> | 41 | #include <QDBusMessage> | ||
40 | #include <QGuiApplication> | 42 | #include <QGuiApplication> | ||
41 | #include <QQmlComponent> | 43 | #include <QQmlComponent> | ||
42 | #include <QQmlContext> | 44 | #include <QQmlContext> | ||
43 | #include <QQmlEngine> | 45 | #include <QQmlEngine> | ||
44 | #include <QQuickItem> | 46 | #include <QQuickItem> | ||
45 | #include <QQuickView> | 47 | #include <QQuickView> | ||
46 | #include <QQuickWindow> | 48 | #include <QQuickWindow> | ||
49 | #include <QTimer> | ||||
47 | // xkbcommon | 50 | // xkbcommon | ||
48 | #include <xkbcommon/xkbcommon.h> | 51 | #include <xkbcommon/xkbcommon.h> | ||
49 | 52 | | |||
50 | using namespace KWayland::Server; | 53 | using namespace KWayland::Server; | ||
51 | 54 | | |||
52 | namespace KWin | 55 | namespace KWin | ||
53 | { | 56 | { | ||
54 | 57 | | |||
55 | KWIN_SINGLETON_FACTORY(VirtualKeyboard) | 58 | KWIN_SINGLETON_FACTORY(VirtualKeyboard) | ||
56 | 59 | | |||
57 | VirtualKeyboard::VirtualKeyboard(QObject *parent) | 60 | VirtualKeyboard::VirtualKeyboard(QObject *parent) | ||
58 | : QObject(parent) | 61 | : QObject(parent) | ||
59 | { | 62 | { | ||
63 | m_floodTimer = new QTimer(this); | ||||
64 | m_floodTimer->setSingleShot(true); | ||||
65 | m_floodTimer->setInterval(250); | ||||
60 | // this is actually too late. Other processes are started before init, | 66 | // this is actually too late. Other processes are started before init, | ||
61 | // so might miss the availability of text input | 67 | // so might miss the availability of text input | ||
62 | // but without Workspace we don't have the window listed at all | 68 | // but without Workspace we don't have the window listed at all | ||
63 | connect(kwinApp(), &Application::workspaceCreated, this, &VirtualKeyboard::init); | 69 | connect(kwinApp(), &Application::workspaceCreated, this, &VirtualKeyboard::init); | ||
64 | } | 70 | } | ||
65 | 71 | | |||
66 | VirtualKeyboard::~VirtualKeyboard() = default; | 72 | VirtualKeyboard::~VirtualKeyboard() = default; | ||
67 | 73 | | |||
▲ Show 20 Lines • Show All 73 Lines • ▼ Show 20 Line(s) | 144 | m_waylandHintsConnection = connect(t, &TextInputInterface::contentTypeChanged, this, | |||
141 | } | 147 | } | ||
142 | ); | 148 | ); | ||
143 | m_waylandResetConnection = connect(t, &TextInputInterface::requestReset, qApp->inputMethod(), &QInputMethod::reset); | 149 | m_waylandResetConnection = connect(t, &TextInputInterface::requestReset, qApp->inputMethod(), &QInputMethod::reset); | ||
144 | m_waylandEnabledConnection = connect(t, &TextInputInterface::enabledChanged, this, | 150 | m_waylandEnabledConnection = connect(t, &TextInputInterface::enabledChanged, this, | ||
145 | [] { | 151 | [] { | ||
146 | qApp->inputMethod()->update(Qt::ImQueryAll); | 152 | qApp->inputMethod()->update(Qt::ImQueryAll); | ||
147 | } | 153 | } | ||
148 | ); | 154 | ); | ||
149 | // TODO: calculate overlap | 155 | | ||
150 | t->setInputPanelState(m_inputWindow->isVisible(), QRect(0, 0, 0, 0)); | 156 | auto newClient = waylandServer()->findAbstractClient(waylandServer()->seat()->focusedTextInputSurface()); | ||
157 | // Reset the old client virtual keybaord geom if necessary | ||||
davidedmundson: If we move from one subsurface to another we would get into this path with the same client.
It… | |||||
158 | // Old and new clients could be the same if focus moves between subsurfaces | ||||
159 | if (newClient != m_trackedClient) { | ||||
160 | if (m_trackedClient) { | ||||
161 | m_trackedClient->setVirtualKeyboardGeometry(QRect()); | ||||
davidedmundson: m_trackedClient = t; | |||||
mart: t is not an abstractclient but a surface? | |||||
davidedmundson: I meant:
m_trackedClient = waylandServer()->findAbstractClient(t);
| |||||
162 | } | ||||
163 | m_trackedClient = newClient; | ||||
164 | } | ||||
165 | | ||||
166 | m_trackedClient = waylandServer()->findAbstractClient(waylandServer()->seat()->focusedTextInputSurface()); | ||||
167 | | ||||
168 | updateInputPanelState(); | ||||
151 | } else { | 169 | } else { | ||
152 | m_waylandShowConnection = QMetaObject::Connection(); | 170 | m_waylandShowConnection = QMetaObject::Connection(); | ||
153 | m_waylandHideConnection = QMetaObject::Connection(); | 171 | m_waylandHideConnection = QMetaObject::Connection(); | ||
154 | m_waylandHintsConnection = QMetaObject::Connection(); | 172 | m_waylandHintsConnection = QMetaObject::Connection(); | ||
155 | m_waylandSurroundingTextConnection = QMetaObject::Connection(); | 173 | m_waylandSurroundingTextConnection = QMetaObject::Connection(); | ||
156 | m_waylandResetConnection = QMetaObject::Connection(); | 174 | m_waylandResetConnection = QMetaObject::Connection(); | ||
157 | m_waylandEnabledConnection = QMetaObject::Connection(); | 175 | m_waylandEnabledConnection = QMetaObject::Connection(); | ||
158 | } | 176 | } | ||
Show All 12 Lines | |||||
171 | connect(m_inputWindow->rootObject(), &QQuickItem::childrenRectChanged, m_inputWindow.data(), | 189 | connect(m_inputWindow->rootObject(), &QQuickItem::childrenRectChanged, m_inputWindow.data(), | ||
172 | [this] { | 190 | [this] { | ||
173 | if (!m_inputWindow) { | 191 | if (!m_inputWindow) { | ||
174 | return; | 192 | return; | ||
175 | } | 193 | } | ||
176 | m_inputWindow->setMask(m_inputWindow->rootObject()->childrenRect().toRect()); | 194 | m_inputWindow->setMask(m_inputWindow->rootObject()->childrenRect().toRect()); | ||
177 | } | 195 | } | ||
178 | ); | 196 | ); | ||
179 | connect(qApp->inputMethod(), &QInputMethod::visibleChanged, m_inputWindow.data(), | 197 | | ||
180 | [this] { | 198 | connect(qApp->inputMethod(), &QInputMethod::visibleChanged, this, &VirtualKeyboard::updateInputPanelState); | ||
181 | m_inputWindow->setVisible(qApp->inputMethod()->isVisible()); | 199 | | ||
182 | if (qApp->inputMethod()->isVisible()) { | 200 | connect(m_inputWindow->rootObject(), &QQuickItem::childrenRectChanged, this, &VirtualKeyboard::updateInputPanelState); | ||
If this changes whilst our text input has focus, we'll cache the wrong m_trackedClientOriginalMaxState davidedmundson: If this changes whilst our text input has focus, we'll cache the wrong… | |||||
hmm, that should happen only when inputPanelHasBeenOpened? mart: hmm, that should happen only when inputPanelHasBeenOpened?
(that means, the keyboard has been… | |||||
183 | m_inputWindow->setMask(m_inputWindow->rootObject()->childrenRect().toRect()); | | |||
184 | } | | |||
185 | if (waylandServer()) { | | |||
186 | if (auto t = waylandServer()->seat()->focusedTextInput()) { | | |||
187 | // TODO: calculate overlap | | |||
188 | t->setInputPanelState(m_inputWindow->isVisible(), QRect(0, 0, 0, 0)); | | |||
189 | } | | |||
190 | } | | |||
191 | } | | |||
192 | ); | | |||
193 | } | 201 | } | ||
194 | 202 | | |||
195 | void VirtualKeyboard::setEnabled(bool enabled) | 203 | void VirtualKeyboard::setEnabled(bool enabled) | ||
196 | { | 204 | { | ||
197 | if (m_enabled == enabled) { | 205 | if (m_enabled == enabled) { | ||
198 | return; | 206 | return; | ||
199 | } | 207 | } | ||
200 | m_enabled = enabled; | 208 | m_enabled = enabled; | ||
Show All 20 Lines | 228 | if (m_enabled) { | |||
221 | m_sni->setIconByName(QStringLiteral("input-keyboard-virtual-on")); | 229 | m_sni->setIconByName(QStringLiteral("input-keyboard-virtual-on")); | ||
222 | m_sni->setToolTipTitle(i18n("Virtual Keyboard is enabled.")); | 230 | m_sni->setToolTipTitle(i18n("Virtual Keyboard is enabled.")); | ||
223 | } else { | 231 | } else { | ||
224 | m_sni->setIconByName(QStringLiteral("input-keyboard-virtual-off")); | 232 | m_sni->setIconByName(QStringLiteral("input-keyboard-virtual-off")); | ||
225 | m_sni->setToolTipTitle(i18n("Virtual Keyboard is disabled.")); | 233 | m_sni->setToolTipTitle(i18n("Virtual Keyboard is disabled.")); | ||
226 | } | 234 | } | ||
227 | } | 235 | } | ||
228 | 236 | | |||
237 | void VirtualKeyboard::updateInputPanelState() | ||||
238 | { | ||||
239 | if (!waylandServer()) { | ||||
240 | return; | ||||
241 | } | ||||
242 | | ||||
243 | auto t = waylandServer()->seat()->focusedTextInput(); | ||||
244 | | ||||
245 | if (!t) { | ||||
246 | return; | ||||
Can you put this and the next condition in the beginning and: if(!condition) { return; } romangg: Can you put this and the next condition in the beginning and:
```
if(!condition) {
return… | |||||
247 | } | ||||
248 | | ||||
249 | const bool inputPanelHasBeenClosed = m_inputWindow->isVisible() && !qApp->inputMethod()->isVisible(); | ||||
250 | if (inputPanelHasBeenClosed && m_floodTimer->isActive()) { | ||||
251 | return; | ||||
252 | } | ||||
253 | m_floodTimer->start(); | ||||
254 | | ||||
255 | m_inputWindow->setVisible(qApp->inputMethod()->isVisible()); | ||||
256 | | ||||
257 | if (qApp->inputMethod()->isVisible()) { | ||||
258 | m_inputWindow->setMask(m_inputWindow->rootObject()->childrenRect().toRect()); | ||||
259 | } | ||||
260 | | ||||
261 | if (m_inputWindow->isVisible() && m_trackedClient && m_inputWindow->rootObject()) { | ||||
262 | const QRect inputPanelGeom = m_inputWindow->rootObject()->childrenRect().toRect().translated(m_inputWindow->geometry().topLeft()); | ||||
263 | | ||||
264 | m_trackedClient->setVirtualKeyboardGeometry(inputPanelGeom); | ||||
265 | | ||||
266 | t->setInputPanelState(true, QRect(0, 0, 0, 0)); | ||||
267 | | ||||
268 | } else { | ||||
269 | if (inputPanelHasBeenClosed && m_trackedClient) { | ||||
270 | m_trackedClient->setVirtualKeyboardGeometry(QRect()); | ||||
271 | } | ||||
I would expect these two lines need swapping, otherwise you're restoring maximise before we're adjusted the client area. davidedmundson: I would expect these two lines need swapping, otherwise you're restoring maximise before we're… | |||||
This comment is done Writing that explicitly as phab make it look like we should swap them again! davidedmundson: This comment is done
Writing that explicitly as phab make it look like we should swap them… | |||||
272 | | ||||
273 | t->setInputPanelState(false, QRect(0, 0, 0, 0)); | ||||
274 | } | ||||
275 | } | ||||
276 | | ||||
229 | void VirtualKeyboard::show() | 277 | void VirtualKeyboard::show() | ||
230 | { | 278 | { | ||
231 | if (m_inputWindow.isNull() || !m_enabled) { | 279 | if (m_inputWindow.isNull() || !m_enabled) { | ||
232 | return; | 280 | return; | ||
233 | } | 281 | } | ||
234 | m_inputWindow->setGeometry(screens()->geometry(screens()->current())); | 282 | m_inputWindow->setGeometry(screens()->geometry(screens()->current())); | ||
235 | qApp->inputMethod()->show(); | 283 | qApp->inputMethod()->show(); | ||
236 | } | 284 | } | ||
▲ Show 20 Lines • Show All 213 Lines • Show Last 20 Lines |
If we move from one subsurface to another we would get into this path with the same client.
It might be worth doing